Сопоставить подстроки между несколькими фреймами данных и взвешенным значением суммы в отдельном столбце - PullRequest
4 голосов
/ 26 мая 2019

Я пытаюсь классифицировать список строк с именем keywords на основе того, содержат ли они 1 или более подстрок из другого DataFrame.

Подстрока взвешивается таким образом, чтобы в конце каждая keyword имела вес примерно в 4 столбцах. Эти столбцы будут называться loan, mortgage, accounts и cards.

Я пытался использовать join(), concat() и merge() для объединения нескольких фреймов данных, а также пытался использовать where(), isin() и contains().

Я несколько раз подходил к достижению желаемой цели, но ему мешали либо ошибки, либо масштабируемость. У меня ограниченный опыт работы с Python и Pandas, поэтому может быть совершенно другой подход, который я не рассматривал.

import pandas as pd

df = pd.read_csv('Inputs/keyword.csv', header=0)
df['loan'] = 0
df2 = pd.read_csv('Essentials/Groups/loans.csv', header=0)

#review the data in df
print(df.head())

output:
           keywords  search_volume  loan
0             loans         132000     0
1    funding circle          81000     0
2        government          36000     0
3  short term loans          30000     0
4           company          27000     0

В приведенном выше коде я загружаю свой список ключевых слов, включая связанный объем поиска. В настоящее время в нем нет столбца ссуды, поэтому я добавил столбец со значением по умолчанию, равным 0.

Ниже у меня есть еще один DataFrame, который включает в себя список терминов и соответствующее взвешивание. Я произвольно выбрал целое число 5, которое хотел бы добавить к существующему итогу.

#review the data in df2
print(df2.head())

output:
      terms  weight
0      loan       5
1     loans       5
2  personal       3
3  business       3
4       apr       4

Есть проблема, которую я обнаружил, но не знаю, как ее решить.

Мой список включает в себя loan и loans. Хорошо, что у меня есть эти дубликаты на моем конце. Однако строка 3 имеет значение «краткосрочные кредиты» и помечена как «Ложь».

Поскольку термины loan и loans оба появляются в short term loans, я ожидал, что он будет помечен как True. Я попытался отменить оператор .isin(), чтобы выполнить поиск df2['terms'] в df['keywords'], но результат оказался таким же.

#review the true/false for overlaps
print(df['keywords'].isin(df2['terms']).head())

output:
0     True
1    False
2    False
3    False
4    False

Наконец, как только мы исправим эту логическую проблему, я не знаю, как векторизовать изменение суммы в df['loan'] на основе совпадений. Я стараюсь избегать циклов, потому что я ожидаю, что список ключевых слов будет содержать около 100 000 строк и каждая категория данных может содержать 1000 терминов.

Желаемый результат будет выглядеть примерно так:

output:
           keywords  search_volume  loan  mortgage  accounts  cards
0             loans         132000     10        0         0      0
1    funding circle          81000     0         0         0      0
2        government          36000     0         0         0      0
3  short term loans          30000     10        0         0      0
4           company          27000     0         0         0      0

Ответы [ 2 ]

4 голосов
/ 26 мая 2019

Учитывая df1 как:

           keywords  search_volume
0             loans         132000
1    funding circle          81000
2        government          36000
3  short term loans          30000
4           company          27000

Вы можете воспользоваться помощью series.str.extract() и df.assign(), мы можем сделать что-то вроде:

d=df2.set_index('terms')['weight']
pat=r'({})'.format('|'.join(df2.terms))
#'(loan|loans|personal|business|apr)'
df1=df1.assign(**{'term_match':df1.keywords.str.extract(pat,expand=False),
              'weight':df1.keywords.str.extract(pat,expand=False).map(d)})
print(df1)

выход

           keywords  search_volume term_match  weight
0             loans         132000       loan     5.0
1    funding circle          81000        NaN     NaN
2        government          36000        NaN     NaN
3  short term loans          30000       loan     5.0
4           company          27000        NaN     NaN

EDIT Чтобы найти все подходящие строки, обновим df2 как: df2.loc[5]=['term',3] только для тестирования.

Затем с помощью series.str.findall():

s=df1.keywords.str.findall(pat)
df1=df1.assign(**{'term_match':s.apply(','.join),
              'weight':s.apply(lambda x: sum([d.get(item,item)  for item in x]))})
print(df1)

           keywords  search_volume  loan term_match  weight
0             loans         132000     0       loan       5
1    funding circle          81000     0                  0
2        government          36000     0                  0
3  short term loans          30000     0  term,loan       8
4           company          27000     0                  0
3 голосов
/ 26 мая 2019

Вот способ узнать количество совпадений. Это может помочь вам в правильном направлении.

Сначала взорвите наши строки, чтобы разделить строки, используя эту функцию :

df = explode_str(df, 'keywords', ' ')

     keywords  search_volume  loan
0       loans         132000     0
1     funding          81000     0
1      circle          81000     0
2  government          36000     0
3       short          30000     0
3        term          30000     0
3       loans          30000     0
4     company          27000     0

Затем мы используем модуль difflib для получения ближайших совпадений, например loan с loans:

import difflib

df['loan'] = df.set_index('keywords').index.map(lambda x: difflib.get_close_matches(x, df2.set_index('terms').index))

     keywords  search_volume           loan
0       loans         132000  [loans, loan]
1     funding          81000             []
1      circle          81000             []
2  government          36000             []
3       short          30000             []
3        term          30000             []
3       loans          30000  [loans, loan]
4     company          27000             []

Затем мы сгруппируемся по индексу, чтобы вернуть наш исходный кадр данных и посчитать длину совпадений:

df = df.groupby(df.index).agg({'keywords':' '.join,
                               'search_volume':'last',
                               'loan':'last'})

df['count'] = df['loan'].str.len()

           keywords  search_volume           loan  count
0             loans         132000  [loans, loan]      2
1    funding circle          81000             []      0
2        government          36000             []      0
3  short term loans          30000  [loans, loan]      2
4           company          27000             []      0

Осталось только умножить счет на вес, и вы получили то, что хотели.

...