Ejemplo n.º 1
0
 def test_word_probability(self):
     ''' test the word probability calculation '''
     spell = SpellChecker()
     # if the default load changes so will this...
     num = spell.word_frequency['the']
     denom = spell.word_frequency.total_words
     self.assertEqual(spell.word_probability('the'), num / denom)
Ejemplo n.º 2
0
def demo_spellchecker():
    """演示如何使用spellchecker库
    官方介绍文档 pyspellchecker · PyPI: https://pypi.org/project/pyspellchecker/
    190909周一15:58,from 陈坤泽
    """
    # 1 创建对象
    # 可以设置语言、大小写敏感、拼写检查的最大距离
    #   默认'en'英语,大小写不敏感
    spell = SpellChecker()
    # 如果是英语,SpellChecker会自动加载语言包site-packages\spellchecker\resources\en.json.gz,大概12万个词汇,包括词频权重
    d = spell.word_frequency  # 这里的d是WordFrequency对象,其底层用了Counter类进行数据存储
    dprint(d.unique_words, d.total_words)  # 词汇数,权重总和

    # 2 修改词频表 spell.word_frequency
    dprint(d['ckz'])  # 不存在的词汇直接输出0
    d.add('ckz')  # 可以添加ckz词汇的一次词频
    d.load_words(['ckz', 'ckz', 'lyb'])  # 可以批量添加词汇
    dprint(d['ckz'], d['lyb'])  # d['ckz']=3  d['lyb']=1
    d.load_words(['ckz'] * 100 + ['lyb'] * 500)  # 可以用这种技巧进行大权重的添加
    dprint(d['ckz'], d['lyb'])  # d['ckz']=103  d['lyb']=501

    # 同理,去除也有remove和remove_words两种方法
    d.remove('ckz')
    # d.remove_words(['ckz', 'lyb'])  # 不过注意不能删除已经不存在的key('ckz'),否则会报KeyError
    dprint(d['ckz'], d['lyb'])  # d['ckz']=0  d['lyb']=501
    # remove是完全去除单词,如果只是要减权重可以访问底层的_dictionary对象操作
    d._dictionary['lyb'] -= 100  # 当然不太建议直接访问下划线开头的成员变量~~
    dprint(d['lyb'])  # ['lyb']=401

    # 还可以按阈值删除词频不超过设置阈值的词汇
    d.remove_by_threshold(5)

    # 3 spell的基本功能
    # (1)用unknown可以找到可能拼写错误的单词,再用correction可以获得最佳修改意见
    misspelled = spell.unknown(['something', 'is', 'hapenning', 'here'])
    dprint(misspelled)  # misspelled<set>={'hapenning'}

    for word in misspelled:
        # Get the one `most likely` answer
        dprint(spell.correction(word))  # <str>='happening'
        # Get a list of `likely` options
        dprint(spell.candidates(
            word))  # <set>={'henning', 'happening', 'penning'}

    # 注意默认的spell不区分大小写,如果词库存储了100次'ckz'
    #   此时判断任意大小写形式组合的'CKZ'都是返回原值
    #   例如 spell.correction('ckZ') => 'ckZ'

    # (2)可以通过修改spell.word_frequency影响correction的计算结果
    dprint(d['henning'], d['happening'], d['penning'])
    # d['henning']<int>=53    d['happening']<int>=4538    d['penning']<int>=23
    d._dictionary['henning'] += 10000
    dprint(spell.correction('hapenning'))  # <str>='henning'

    # (3)词汇在整个字典里占的权重
    dprint(spell.word_probability('henning'))  # <float>=0.0001040741914298211
Ejemplo n.º 3
0
    def correctDLWord(self, misspelled):
        spellDL = SpellChecker(language=None)
        spellDL.word_frequency.load_text_file(self.text_file_path)
        #spellDL.export("DL_dictionary.gz", gzipped = True)
        misspelled_lower = misspelled.lower()

        if misspelled_lower in spellDL:
            return (misspelled, 1.0)
        else:
            correct = spellDL.correction(misspelled_lower)
            prob = spellDL.word_probability(misspelled_lower, total_words=1)
            return (correct, prob)
Ejemplo n.º 4
0
    def correctListWord(self, misspelled, fig_text):
        if fig_text != []:
            spellList = SpellChecker(language=None)  # load default dictionary
            spellList.word_frequency.load_words(fig_text)
            misspelled_lower = misspelled.lower()

            if misspelled_lower in spellList:
                return (misspelled, 1.0)
            else:
                correct = spellList.correction(misspelled_lower)
                prob = spellList.word_probability(misspelled_lower,
                                                  total_words=1)
                return (correct, prob)
        else:
            return (None, 0.0)
    def xor_strings(self):
        puntaje_final_strings = []
        file_read = open(self.file_name, "r")
        out = open("salida", "w")
        spell = SpellChecker()
        for line in file_read:
            #print(line)
            cadena_hex = line.rstrip("\n")
            cadena = cadena_hex.decode("hex")
            print(cadena)
            res = ''
            puntaje_final = 0
            puntaje_final = []
            for i in range(256):
                for j in cadena:
                    res_byte = ord(j) ^ i
                    res += chr(res_byte)
                puntaje_actual = analiza_frecuencia(res)
                puntaje_final.append((puntaje_actual, res, i))
                res = ''
            ult = heapq.nlargest(10, puntaje_final)

            puntaje_actual = 0
            for i in range(10):
                palabras = ult[i][1].split(' ')
                mejor_palabra = spell.known(palabras)
                if (mejor_palabra):
                    for palabra in mejor_palabra:
                        puntaje_actual += spell.word_probability(palabra)
                    puntaje_final_strings.append(
                        (puntaje_actual, ult[i][1], ult[i][2]))

        ult = heapq.nlargest(1, puntaje_final_strings)
        print(ult[0][1])
        file_read.close()
        out.close()
Ejemplo n.º 6
0
class Corrector:
    def __init__(self,
                 lenguaje,
                 diccionario=None,
                 distancia=2,
                 tokenizador=None):
        """
        Constructor por defecto de la clase `Corrector`. Esta clase se \
        encarga de realizar corrección ortográfica sobre textos.

        :param lenguaje: Lenguaje de los textos a los que se les va a \
            aplicar corrección ortográfica. Para mayor información, consultar \
            la sección de \
            :ref:`Lenguajes soportados <seccion_lenguajes_soportados>`.
        :type lenguaje: str
        :param diccionario: Diccionario (o string con ubicación del archivo \
            JSON que lo contiene), o lista que permite modificar y agregar \
            palabras. Si es una lista, contiene las palabras que serán \
            consideradas como válidas o correctas. \
            Si es un diccionario, las llaves del diccionario son las palabras \
            que serán consideradas como válidas o correctas, y los valores \
            del diccionario son las frecuencias de cada palabra en el \
            diccionario. Las frecuencias son utilizadas como criterio de \
            desempate, cuando una palabra incorrecta tiene más de una palabra \
            candidata para la corrección. Si se deja este \
            parámetro como `None`, se cargará el diccionario por defecto que \
            trae la librería `spellchecker` para el lenguaje determinado.
        :type diccionario: dict, list, str, opcional
        :param distancia: Máxima distancia de \
            Levenshtein que puede haber entre una palabra incorrecta (o no \
            reconocida) y las palabras del diccionario para determinar si \
            hay palabras candidatas para realizar la corrección. \
            Valor por defecto `2`.
        :type distancia: int
        :param tokenizador: Objeto encargado de la tokenización y \
            detokenización de textos. Si el valor es `None`, se cargará por \
            defecto una instancia de la clase `TokenizadorNLTK`. Valor por \
            defecto `None`
        :type tokenizador: object, opcional
        """
        # Definir lenguaje del corrector ortográfico
        self.establecer_lenguaje(lenguaje)
        # Inicializar corrector
        self.iniciar_corrector(diccionario)
        self.establecer_distancia(distancia)
        self.tokenizador = (TokenizadorNLTK()
                            if tokenizador is None else tokenizador)

    def establecer_lenguaje(self, lenguaje):
        """
        Permite definir o cambiar el lenguaje de los textos sobre los cuales \
        van a aplicarse el objeto de la clase `Corrector`.

        :param lenguaje: Lenguaje de los textos a los que se les va a \
            aplicar corrección ortográfica. Para mayor información, consultar \
            la sección de \
            :ref:`Lenguajes soportados <seccion_lenguajes_soportados>`.
        :type lenguaje: str
        """
        self.lenguaje = definir_lenguaje(lenguaje)

    def iniciar_corrector(self, diccionario):
        """
        Inicializa el objeto de la clase `SpellChecker` de la librería \
        spellchecker, para el lenguaje definido previamente, y lo asigna al \
        atributo "corrector" del objeto de clase `Corrector`.

        :param diccionario: Diccionario (o string con ubicación del archivo \
            JSON que lo contiene), o lista que permite modificar y agregar \
            palabras. Si es una lista, contiene las palabras que serán \
            consideradas como válidas o correctas. \
            Si es un diccionario, las llaves del diccionario son las palabras \
            que serán consideradas como válidas o correctas, y los valores \
            del diccionario son las frecuencias de cada palabra en el \
            diccionario. Las frecuencias son utilizadas como criterio de \
            desempate, cuando una palabra incorrecta tiene más de una palabra \
            candidata para la corrección. Si se deja este \
            parámetro como `None`, se cargará el diccionario por defecto que \
            trae la librería `spellchecker` para el lenguaje determinado.
        :type diccionario: dict, list, str, opcional
        """
        self.corrector = None
        if self.lenguaje is not None:
            if isinstance(diccionario, str):
                self.corrector = SpellChecker(local_dictionary=diccionario)
            elif type(diccionario) in [dict, list]:
                self.corrector = SpellChecker(language=self.lenguaje)
                self.actualizar_diccionario(diccionario)
            else:
                self.corrector = SpellChecker(language=self.lenguaje)

    def establecer_distancia(self, distancia):
        """
        Establece la distancia máxima que utilizará el algoritmo de corrección\
         de ortografía para determinar si hay palabras candidatas para \
        corregir una palabra incorrecta o no reconocida.

        :param distancia: Máxima distancia de \
            Levenshtein que puede haber entre una palabra incorrecta (o no \
            reconocida) y las palabras del diccionario para determinar si \
            hay palabras candidatas para realizar la corrección. \
            Valor por defecto `2`.
        :type distancia: int
        """
        if self.corrector is not None:
            self.corrector.distance = distancia

    def actualizar_diccionario(self, diccionario):
        """
        Actualiza el diccionario que contiene las palabras válidas o \
        reconocidas disponibles para realizar la corrección ortográfica. Las \
        palabras contenidas en el argumento `diccionario` de esta función \
        serán añadidas (o sus frecuencias serán actualizadas) en el \
        diccionario que ya existe en el objeto de la clase `Corrector`.

        :param diccionario: Diccionario (o string con ubicación del archivo \
            JSON que lo contiene), o lista que permite modificar y agregar \
            palabras. Si es una lista, contiene las palabras que serán \
            consideradas como válidas o correctas. \
            Si es un diccionario, las llaves del diccionario son las palabras \
            que serán consideradas como válidas o correctas, y los valores \
            del diccionario son las frecuencias de cada palabra en el \
            diccionario. Las frecuencias son utilizadas como criterio de \
            desempate, cuando una palabra incorrecta tiene más de una palabra \
            candidata para la corrección. Si se deja este \
            parámetro como `None`, se cargará el diccionario por defecto que \
            trae la librería `spellchecker` para el lenguaje determinado.
        :type diccionario: dict, list, str, opcional
        """
        if isinstance(diccionario, str):
            diccionario = json.load(open(diccionario))
        if isinstance(diccionario, list):
            diccionario = [palabra.lower() for palabra in diccionario]
            self.corrector.word_frequency.load_words(diccionario)
        elif isinstance(diccionario, dict):
            self.quitar_palabras(list(diccionario.keys()))
            for key in diccionario.keys():
                self.corrector.word_frequency.load_words([key.lower()] *
                                                         diccionario[key])
        else:
            pass

    def quitar_palabras(self, palabras):
        """
        Quita del diccionario del corrector una o más palabras \
        proporcionadas en el argumento `palabras`, haciendo que estas ya no \
        sean reconocidas como palabras válidas o correctas al momento de \
        hacer corrección ortográfica.

        :param palabras: Palabra o lista de palabras que se \
            desean quitar del diccionario del objeto de la clase `Corrector`, \
            para que no sean reconocidas como correctas al momento de hacer \
            la corrección ortográfica.
        :type palabras: str, list
        """
        if isinstance(palabras, str):
            palabras = [palabras]
        # Quitar de la lista palabras que no estén en el diccionario
        palabras = [p for p in palabras if self.frecuencia_palabra(p) > 0]
        if len(palabras) > 0:
            self.corrector.word_frequency.remove_words(palabras)

    def agregar_palabras(self, palabras):
        """
        Añade al diccionario del corrector una o más palabras proporcionadas \
        en el argumento `palabras`, haciendo que estas sean reconocidas como \
        palabras válidas o correctas al momento de hacer corrección \
        ortográfica.

        :param palabras: Palabra o lista de palabras que se \
            desean quitar del diccionario del objeto de la clase `Corrector`, \
            para que no sean reconocidas como correctas al momento de hacer \
            la corrección ortográfica.
        :type palabras: str, list
        """
        if isinstance(palabras, str):
            palabras = [palabras]
        self.actualizar_diccionario(palabras)

    def palabras_conocidas(self, texto):
        """
        A partir de un texto de entrada, devuelve un conjunto (objeto de \
        clase `set` de Python) con las palabras del texto que se reconocen \
        por estar presentes en el diccionario del corrector.

        :param texto: Texto para el que se desean hayar las palabras \
            conocidas.
        :type texto: str
        :return: (set) Conjunto de palabras conocidas presentes en el texto \
            de entrada.
        """
        tokens = self.tokenizador.tokenizar(texto)
        return self.corrector.known(tokens)

    def palabras_desconocidas(self, texto):
        """
        A partir de un texto de entrada, devuelve un conjunto (objeto de \
        clase `set` de Python) con las palabras del texto que no están \
        incluidas en el diccionario del corrector y por lo tanto no se \
        reconocen.

        :param texto: Texto para el que se desean hallar las palabras \
            desconocidas.
        :type texto: str
        :return: (set) Conjunto de palabras desconocidas presentes en el \
            texto de entrada.
        """
        tokens = self.tokenizador.tokenizar(texto)
        return self.corrector.unknown(tokens)

    def palabras_candidatas(self, palabra):
        """
        Para una palabra de entrada, retorna un conjunto de palabras que \
        podrían ser utilizadas para corregirla. Si la palabra de entrada es \
        correcta (está dentro del diccionario del corrector) o no tienen \
        ninguna palabra candidata con una distancia menor o igual a la \
        establecida en el parámetro `distancia` de la clase `Corrector`, la \
        función devolverá la misma palabra de entrada.

        :param palabra: Palabra para la que se quieren conocer palabras \
            candidatas para su corrección ortográfica.
        :type palabra: str
        :return: (set) Conjunto de palabras candidatas para corregir la \
            palabra de entrada.
        """
        return self.corrector.candidates(palabra)

    def frecuencia_palabra(self, palabra):
        """
        Para una palabra de entrada, devuelve la frecuencia de la misma, de \
        acuerdo al diccionario del corrector. Si la palabra es desconocida \
        (no se encuentra en el diccionario), la frecuencia retornada será de \
        cero.

        :param palabra: Palabra para la cual se desea conocer la \
            frecuencia de aparición en el diccionario del corrector.
        :type palabra: str
        :return: (int) Número mayor o igual a cero que indica la frecuencia \
            de la palabra consultada en el diccionario del corrector.
        """
        return self.corrector[palabra]

    def probabilidad_palabra(self, palabra):
        """
        Para una palabra de entrada, devuelve la probabilidad de aparición \
        entendida como su frecuencia sobre la suma de las \
        frecuencias de todas las palabras disponibles, de acuerdo al \
        diccionario del corrector. Si la palabra es desconocida (no se \
        encuentra en el diccionario), la probabilidad retornada será de cero.

        :param palabra: Palabra para la cual se desea conocer la \
            probabilidad de aparición en el diccionario del corrector.
        :type palabra: str
        :return: (float) Probabilidad, entre 0 y 1, de aparición de la \
            palabra.
        """
        return self.corrector.word_probability(palabra)

    def correccion_ortografia(self, texto, limpieza=False):
        """
        Realiza corrección ortográfica sobre un texto de entrada, \
        identificando las palabras que no están en el diccionario del \
        corrector y cambiándolas por su candidata más frecuente o probable, \
        siempre y cuando haya por lo menos una palabra candidata que cumpla \
        con la máxima distancia de Levenshtein permitida.

        :param texto: Texto al cual se desea aplicar corrección \
            ortográfica.
        :param limpieza: Define si se desea hacer una limpieza \
            básica (aplicando la función `limpieza_basica` del módulo \
            `limpieza`) al texto antes de aplicar la corrección ortográfica.\
            Valor por defecto `False`.
        :type limpieza: bool, opcional
        :return: (str) Texto de entrada luego de la corrección ortográfica.
        """
        if limpieza:
            # Limpieza básica del texto para que no afecte la corrección
            texto = limpieza_basica(texto, quitar_numeros=False)
        lista_palabras = self.tokenizador.tokenizar(texto)
        desconocidas = self.corrector.unknown(lista_palabras)
        texto_corregido = [
            self.corrector.correction(p)
            if len(p) > 1 and p in desconocidas else p for p in lista_palabras
        ]
        return self.tokenizador.destokenizar(texto_corregido)
Ejemplo n.º 7
0
            accepted = True
        if len(sentence) < 50 or len(sentence) > 300:
            accepted = False
        if not full_stop:
            accepted = False
        count += 1

    return sentence


sentence = generate_sentence(forwards_model,
                             backwards_model,
                             seed_word=u" analytic ")
sentence = sentence[1:]
words = sentence.split(' ')
corrected_sentence = []
for word in words:
    if word == ' ':
        continue
    corrected_word = spell.correction(word)
    print('word = ', word, ' correction = ', corrected_word, ' probability = ',
          spell.word_probability(corrected_word))
    print('candidates = ', spell.candidates(word))
    if len(spell.known([corrected_word])) == 1 or word.isnumeric():
        corrected_sentence.append(corrected_word)

sentence = ' '.join(corrected_sentence) + '.'
sentence = sentence[0].upper() + sentence[1:]
sentence = sentence.replace(' i ', ' I ')
print(sentence)
Ejemplo n.º 8
0
    def check_spelling(self):
        # requires 'pyspellchecker' package of Python
        # for texts checks if they are spelled correctly
        # if low frequency of suggestion, manual intervention needed
        # manual entry will be stored and used for the next time

        # if ERROR is True, the next code will not be executed
        if self.ERROR == True:
            return

        langs = {
            'en': 'english',
            'fr': 'french',
            'de': 'german',
            'it': 'italian'
        }

        # provide a list of corrections
        corrections_list = []
        corrected_data = []

        # join all texts from self.data in one string
        # easier to later tokenize in a single step
        self.stdout.write('Spell checking ->\n')
        spell = SpellChecker(self.LNG)

        # get stop words for the given language
        stop_words = set(stopwords.words(langs[self.LNG]))

        # list of all tested words to avoid repetitions
        tested_words = {}

        i = 0
        for row in self.DATA:

            i += 1
            if i == 1000:
                self.stdout.write('1k')
                i = 0

            text = row['text']
            words = word_tokenize(text)

            # filter stop words
            words_filtered = [
                w for w in words if w not in stop_words and len(w) > 1
            ]

            # check spelling and correct
            # create set to be faster process with a shorter number of words
            words_filtered = set(words_filtered)
            words_unknown = spell.unknown(words_filtered)

            # this will replace previous input in self.DATA
            words_corrected = []

            for word in words_filtered:

                if len(word) > 3:
                    try:
                        # if already corrected previously
                        # this includes all those with low freq that are not unknown
                        correction = SpellCorrection.objects.get(
                            language=self.LNG, word=word)
                        words_corrected.append(correction.correction)

                    except:
                        # if not we need to check for candidates, if unknown
                        if word in words_unknown:

                            if word in tested_words:
                                # well, if already tested
                                words_corrected.append(tested_words[word])
                                continue

                            candidates = list(spell.candidates(word))

                            # if correction is equal to word well it means that correcting failed
                            if len(candidates) == 1 and word == candidates[0]:
                                corrections_list.append({
                                    'word':
                                    word,
                                    'correction':
                                    ' '.join(candidates),
                                    'prob':
                                    'unknown'
                                })

                            # if many candidates
                            if len(candidates) > 1:
                                corrections_list.append({
                                    'word':
                                    word,
                                    'correction':
                                    ' '.join(candidates),
                                    'prob':
                                    'unknown'
                                })

                            # for correction we take the most probable word
                            words_corrected.append(candidates[0])
                        else:
                            words_corrected.append(word)

                        if self.ARG_MOST_PROBABLE == False and word not in words_unknown:
                            # also calculate frequency of those words
                            if word not in tested_words:
                                prob = spell.word_probability(word) * 10e6

                                if prob < 10:
                                    corrections_list.append({
                                        'word': word,
                                        'correction': word,
                                        'prob': prob
                                    })
                                    tested_words[word] = word

                # not correcting those shorter or equal to length 3
                else:
                    words_corrected.append(word)

            # now correct row in self.DATA
            corrected_row = {
                'text': ' '.join(words_corrected),
                'code': row['code']
            }
            corrected_data.append(corrected_row)

        self.DATA = corrected_data

        # if arg is --most-probable True
        # we assign the most probable candidate
        if self.ARG_MOST_PROBABLE == True:
            return

        # if no corrections in corrections_list no need to go further
        if len(corrections_list) == 0:
            self.stdout.write(
                self.style.SUCCESS("SUCCESS: NO CORRECTIONS REQUIRED"))
            return

        # ask if admin wants to provide corrections in console
        else:
            self.stdout.write(
                self.style.WARNING("%d corrections required" %
                                   len(corrections_list)))
            provide_corrections = input(
                'Do you want to provide spell corrections?\n')

            if provide_corrections == 'Y':
                for cor in corrections_list:
                    self.stdout.write("Which is correct for %s: \n %s\n" %
                                      (cor['word'], cor['correction']))
                    cor_text = input()

                    if cor_text == 'cpy':
                        # keep the same word
                        cor_text = cor['word']

                    elif cor_text == 'ign':
                        # skip correction
                        continue

                    spell_cor_ser = {
                        "language": self.LNG,
                        "word": cor['word'],
                        "correction": cor_text
                    }

                    if spell_cor_ser.is_valid():
                        spell_cor_ser.save()

        # if not most probable
        # we must write CSV file with recommended candidates
        # and those with low freqency
        self.stdout.write("Writing to CSV")
        path = os.path.join(BASE_DIR, 'DATA/exported/corrections.csv')
        with open(path, 'w') as f:
            # write header
            f.write("word;correction;prob\n")
            for e in corrections_list:
                f.write("{};{};{}\n".format(e['word'], e['correction'],
                                            e['prob']))

        self.ERROR = True
        self.ERROR_TEXT = "SOME WORDS REQUIRE SPELL CHECK"
        return False
Ejemplo n.º 9
0
    d_w = sep.join(d_w)
    return [d_w]


s3 = []
for word in s2.split(' '):  # Looping through all the words in the book.
    clean_word = re.sub('[^a-zA-Z]+', '', word).lower()
    if len(clean_word) > 0:
        # Dealing with words with hyphens
        if '-' in word[:-1]:
            s3 += double_word_splitter(word, '-')
        elif '_' in word[:-1]:
            s3 += double_word_splitter(word, '_')
        # Dealing with non-hyphen words
        else:
            if spell.word_probability(clean_word) <= wrong_word_prob_cutoff:
                s3 += get_most_probable_split(word)
            else:
                s3 += [word]
    else:
        s3 += [word]

txt = ' '.join(s3)
f = open(root_dir + filename[:-4] + '.txt', "w")
f.write(txt)
f.close()

# %% Exporting to mp3

engine = pyttsx3.init()
def query_expansion_thesaurus(query_str):
    """
    Params:
        - query_str: Original input query string

    Returns:
        - new_query: List of expanded query terms and weights [ (term, weight), ...]
    """

    query_str = query_str.replace('AND', '').replace('"', '')
    query_terms = set([query_term.strip() for query_term in query_str.split(' ')])

    term_weights = defaultdict(lambda: 0.0)
    new_query_terms = list(query_terms)

    ORIGINAL_TERM_WEIGHT = 1
    SYNONYM_TERM_WEIGHT = 0.5
    MISSPELLING_TERM_WEIGHT = 0.6
    CONTEXT_TERM_WEIGHT = 0.7


    for term in query_terms:
        term_weights[term] = ORIGINAL_TERM_WEIGHT


    # Add synonyms to query
    FRACTION_SYNONYMS = 0.35

    synonyms = []
    for term in query_terms:
        for syn in wordnet.synsets(term):
            num_synonyms = math.ceil(len(syn.lemmas()) * FRACTION_SYNONYMS)

            for l in syn.lemmas()[:num_synonyms]:
                for t in l.name().split("_"):
                    synonyms.append(t)
                    term_weights[t] = max(SYNONYM_TERM_WEIGHT, term_weights[t])

        # Use original query context to find suitable synonyms
        context_syn = lesk(query_str, term)
        if context_syn is not None:
            for l in context_syn.lemmas():
                for t in l.name().split("_"):
                    synonyms.append(t)
                    term_weights[t] = max(CONTEXT_TERM_WEIGHT, term_weights[t])

    new_query_terms.extend(synonyms)


    # Add spelling corrections
    spell = SpellChecker()

    unknown_misspelled_terms = spell.unknown(query_terms)
    for term in unknown_misspelled_terms:
        new_query_terms.append(spell.correction(term))  # Add the most likely correct word

    known_misspelled_terms = spell.known(query_terms)
    for mispelled_term in known_misspelled_terms:
        corrected_terms = spell.known(spell.edit_distance_2(mispelled_term))

        probs = []
        for term in corrected_terms:
            probs.append((term, spell.word_probability(term)))

        probs = sorted(probs, key=lambda x: x[1], reverse=True)
        new_corrected_terms = [term[0] for term in probs]

        new_query_terms.extend(new_corrected_terms[:3])  # Add top 3 corrections
        for t in new_corrected_terms[:3]:
            term_weights[t] = max(MISSPELLING_TERM_WEIGHT, term_weights[t])


    # Preprocess terms and assign weights to the expanded query terms
    new_query = defaultdict(lambda: 0.0)
    for term in set(new_query_terms):
        for new_term in set(util.preprocess_content(term)):
            new_query[new_term] = max(term_weights[term], new_query[new_term])

    new_query = [(term, weight) for term, weight in new_query.items()]

    return new_query