Строка заменить условием - PullRequest
0 голосов
/ 28 апреля 2018

У меня есть два кадра данных панд. Один содержит текст, другой набор терминов, которые я хотел бы найти и заменить в тексте. У меня есть способ сделать это, однако я хотел бы добавить условия. Условия, если термин содержит слово «нет» или «нет» до трех слов ранее, не заменяют.

В приведенном ниже примере ID 2 неправильно заменен в соответствии с вышеуказанными условиями.

Пример текста:

d = {'ID': [1, 2, 3], 'Text': ['here is some random text', 'no such random text, none here', 'more random text']}
text_df = pd.DataFrame(data=d)

Пример терминов:

d = {'Replace_item': ['<RANDOM_REPLACED>', '<HERE_REPLACED>', '<SOME_REPLACED>'], 'Text': ['random', 'here', 'some']}
replace_terms_df = pd.DataFrame(data=d)

Метод замены терминов (с неверным идентификатором 2 в зависимости от условий):

text_df['Text'] = [z.replace(x, y) for (x, y, z) in zip(replace_terms_df.Text, replace_terms_df.Replace_item, text_df.Text)]

Целевой кадр данных (с учетом условий):

d = {'ID': [1, 2, 3], 'Text': ['<HERE_REPLACED> is <SOME_REPLACED> <RANDOM_REPLACED> text', 'no such random text, none here', 'more  <RANDOM_REPLACED> text']}
target_df = pd.DataFrame(data=d)

Пожалуйста, спросите, нужна ли вам ясность. Спасибо.

Ответы [ 2 ]

0 голосов
/ 29 апреля 2018

Проверьте следующий код с помощью регулярного выражения:

import re

# set up the regex pattern
# the words which should be skipped, must be whole word and case-insensitive
ptn_to_skip = re.compile(r'\b(?:no|none)\b', re.IGNORECASE)

# the pattern for mapping
# Note: any regex meta charaters need to be escaped, or it will fail.
ptn_to_map = re.compile(r'\b(' + '|'.join(replace_terms_df.Text.tolist()) + r')\b')

# map from text to Replace_item
terms_map = replace_terms_df.set_index('Text').Replace_item

def adjust_text(x):
    # if 1 - 3 ptn_to_skip found, return x, 
    # otherwise, map the matched group \1 with terms_map
    if 0 < len(ptn_to_skip.findall(x)) <= 3:
        return x
    else:
        return ptn_to_map.sub(lambda y: terms_map[y.group(1)], x)

# do the conversion:
text_df['new_text'] = text_df.Text.apply(adjust_text)

Некоторые заметки:

  • Я преобразовал тексты в replace_terms_df.Text в регулярное выражение. по умолчанию тексты все текстовые без метасимволов регулярных выражений.
  • если есть какие-либо метасимволы регулярных выражений, такие как '$', ']' и т. Д., Вам придется их избегать. regex имеет тенденцию быть медленным, особенно с метасимволами, если у вас большой кусок данных, не предлагайте вам это решение.

Обновление:

Добавлена ​​новая логика для проверки сначала исключенных слов ['no', 'none'], если они совпадают, затем найдите следующие 0-3 слова, которые сами не являются исключенными словами, сохраните их в \ 1, Фактически найденное поисковое слово будет сохранено в \ 2. затем в запасной части регулярного выражения обращайтесь с ними по-другому.

Ниже новый код:

import re

# pattern to excluded words (must match whole-word and case insensitive)
ptn_to_excluded = r'\b(?i:no|none)\b'

# ptn_1 to match the excluded-words ['no', 'none'] and the following maximal 3 words which are not excluded-words
# print(ptn_1)  -->    \b(?i:no|none)\b\s*(?:(?!\b(?i:no|none)\b)\S+\s*){,3}
# where (?:(?!\b(?i:no|none)\b)\S+\s*) matches any words '\S+' which is not in ['no', 'none'] followed by optional white-spaces
# {,3} to specify matches up to 3 words 
ptn_1 = r'{0}\s*(?:(?!{0})\S+\s*){{,3}}'.format(ptn_to_excluded)

# ptn_2 is the list of words you want to convert with your terms_map
# print(ptn_2)    -->    \b(?:random|here|some)\b
ptn_2 = r'\b(?:' + '|'.join(replace_terms_df.Text.tolist()) + r')\b'

# new pattern based on the alternation using ptn_1 and ptn_2
# regex:  (ptn_1)|(ptn_2)
new_ptn = re.compile('({})|({})'.format(ptn_1, ptn_2))

# map from text to Replace_item
terms_map = replace_terms_df.set_index('Text').Replace_item

# regex function to do the convertion
def adjust_map(x):
    return new_ptn.sub(lambda m:  m.group(1) or terms_map[m.group(2)], x)

# do the conversion:
text_df['new_text'] = text_df.Text.apply(adjust_map)

Пояснение:

Я определил два подэлемента:

  • ptn_1: попробуйте сопоставить слова, которые вы хотите исключить, то есть слова «нет», «нет», за которыми следует не более 3 слов, которых нет в [«нет», «нет»]
  • ptn_2: попробуйте сопоставить одно из слов, которое вы хотите преобразовать, на основе replace_terms_df.

Как это работает:

  • с чередованием '|', механизм регулярных выражений будет проверять соответствие ptn_1 перед ptn_2, если ни одно из них не совпадает, исходный текст сохраняется.
  • Соответствующий текст ptn_1 будет сохранен в m.group (1), а результат ptn_2 - в m.group (2)
  • В запасной части. Если m.group (1) не является пустым (что означает, что ptn_1 соответствует), тогда верните m.group (1) (таким образом, эта часть совпадений не затрагивается), в противном случае верните term_map [y.group (2)]

Некоторые тесты ниже:

In []: print(new_ptn)
re.compile('(\\b(?i:no|none)\\b\\s*(?:(?!\\b(?i:no|none)\\b)\\S+\\s*){,3})|(\\b(random|here|some)\\b)')

In[]: for i in [
    'yes, no such a random text'
  , 'yes, no such a a random text'
  , 'no no no such a random text no such here here here no'
 ]: print('{}:\n  [{}]'.format(i, adjust_map(i)))
...:
yes, no such a random text:
  [yes, no such a random text]
yes, no such a a random text:
  [yes, no such a a <RANDOM_REPLACED> text]
no no no such a random text no such here here here no:
  [no no no such a random text no such here here <HERE_REPLACED> no]

Дайте мне знать, если это работает.

Еще рассмотреть:

  • в ptn_1, '\ S +' используется для определения СЛОВА, это будет иметь проблему, если одно из слов будет что-то вроде ', none ', эта предыдущая 'запятая' позволит пропустить (?! \ b (?: no | none)) тест.
  • Фактически, следует ли исключить ', no ', ' "none" '? это повлияет на то, как считаются слова. модификации ptn_to_excluded может быть достаточно.
0 голосов
/ 29 апреля 2018

Начиная с создания словаря заменяемых предметов поможет. Вы можете сделать следующее:

# create a dict
make_dict = replace_terms_df.set_index('Text')['Replace_item'].to_dict()

# this function does the replacement work
def g_val(strin, dic):

    d = []
    if 'none' in strin or 'no' in strin:
        return strin
    else:
        for i in strin.split():
            if i not in dic:
                d.append(i)
            else:
                d.append(dic[i])
        return ' '.join(d)

## apply the function
text_df['new_text'] = text_df['Text'].apply(lambda x: g_val(x, dic=make_dict))

## check output
print(text_df['new_text'])

0    <HERE_REPLACED> is <SOME_REPLACED> <RANDOM_REP...
1                       no such random text, none here
2                          more <RANDOM_REPLACED> text

Объяснение

В функции мы делаем:
1. Если строка не содержит ни одного, или нет, мы возвращаем строку как есть.
2. Если оно не содержит ни одного или нет, мы проверяем, доступно ли слово в словаре, если да, мы возвращаем замененное значение, иначе существующее значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...