Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Панды: приблизительное соединение в одном столбце, точное совпадение в других столбцах

Автор вопроса: CAR ERLОтветов: 1

У меня есть два кадра данных pandas, которые я хочу соединить/объединить точно по количеству столбцов (скажем, 3) и приблизительно, то есть по ближайшему соседу, по одному столбцу (дата). Я также хочу вернуть разницу (дни) между ними. Каждый набор данных содержит около 50 000 строк. Меня больше всего интересует внутреннее соединение, но "остатки" также интересны, хотя и не слишком сложны для получения. Большинство наблюдений "точного совпадения" будут существовать несколько раз в каждом фрейме данных.

Я пытался использовать difflib.get_close_matches для объединения всех из них в виде строк (что глупо, я знаю!), но это не всегда дает точные совпадения. Я полагаю, мне нужно сначала перебрать точные совпадения, а затем найти ближайшие совпадения в этой группе, но я просто не могу понять это правильно...

Кадры данных выглядят примерно так:

df1 = pd.DataFrame({'index': ['a1','a2','a3','a4'], 'col1': ['1232','432','432','123'], 'col2': ['asd','dsa12','dsa12','asd2'], 'col3': ['1','2','2','3'], 'date': ['2010-01-23','2016-05-20','2010-06-20','2008-10-21'],}).set_index('index')

df1
Out[430]:
       col1   col2 col3        date
index
a1     1232    asd    1  2010-01-23
a2      432  dsa12    2  2016-05-20
a3      432  dsa12    2  2010-06-20
a4      123   asd2    3  2008-10-21

df2 = pd.DataFrame({'index': ['b1','b2','b3','b4'], 'col1': ['132','432','432','123'], 'col2': ['asd','dsa12','dsa12','sd2'], 'col3': ['1','2','2','3'], 'date': ['2010-01-23','2016-05-23','2010-06-10','2008-10-21'],}).set_index('index')

df2
Out[434]:
      col1   col2 col3        date
index
b1     132    asd    1  2010-01-23
b2     432  dsa12    2  2016-05-23
b3     432  dsa12    2  2010-06-10
b4     123    sd2    3  2008-10-21

В конце концов, я хочу что-то вроде:

       col1   col2 col3        date diff match_index
index
a1     1232    asd    1  2010-01-23  nan         nan
a2      432  dsa12    2  2016-05-20   -3          b2
a3      432  dsa12    2  2010-06-20   10          b3
a4      123   asd2    3  2008-10-21  nan         nan
a5      123    sd2    3  2008-10-21  nan          b4

или, если это проще, только с внутренним соединением, я бы хотел:

       col1   col2 col3        date diff match_index
index
a2      432  dsa12    2  2016-05-20   -3          b2
a3      432  dsa12    2  2010-06-20   10          b3
  • Возможно, имеет смысл задавать отдельные вопросы для каждой из вещей, которые вы ищете. fmarc 31.05.2016

pythonpandasmergeexact-matchnearest-neighbor

Ответы

Ответ 1 · рейтинг 2 · Gene Burinsky

Я не уверен, подходит ли это. Он достигает более или менее того, что вы хотите, но на самом деле не выполняет слияние. Он следует той же идее, что и этот вопрос за исключением того, что вместо подмножества df1 на основе только одного столбца здесь мы сопоставляем несколько столбцов, используя groupby, и делаем это для обоих фреймов данных. ЕСЛИ вы хотите явно включить команду merge и довольны внутренним соединением, проверьте самую нижнюю часть ответа, там есть фрагмент для этого.

import pandas as pd
from sklearn.neighbors import NearestNeighbors

    def find_nearest(group, df2, groupname):
        try:
            match = df2.groupby(groupname).get_group(group.name)
            match['date'] = pd.to_datetime(match.date, unit = 'D')
            nbrs = NearestNeighbors(1).fit(match['date'].values[:, None])
            dist, ind = nbrs.kneighbors(group['date'].values[:, None])

            group['date1'] = group['date']
            group['date'] = match['date'].values[ind.ravel()]
            group['diff'] = (group['date1']-group['date'])
            group['match_index'] = match.index[ind.ravel()]
            return group
        except KeyError:
            return group

    #change dates from string to datetime
    df1['date'] = pd.to_datetime(df1.date, unit = 'D')
    df2['date'] = pd.to_datetime(df2.date, unit = 'D')

    #find closest dates and differences
    keys = ['col1', 'col2', 'col3']
    df1_mod = df1.groupby(keys).apply(find_nearest, df2, keys)

    #fill unmatched dates
    df1_mod.date1.fillna(df1_mod.date, inplace=True)

    df2_mod = df2.groupby(keys).apply(find_nearest, df1, keys)
    df2_mod.date1.fillna(df2_mod.date, inplace=True)

    #drop original column
    df1_mod.drop('date', inplace=True, axis=1)
    df1_mod.rename(columns = {'date1':'date'}, inplace=True)

    df2_mod.drop('date', inplace=True, axis=1)
    df2_mod.rename(columns = {'date1':'date'}, inplace=True)
    df2_mod['diff'] = -df2_mod['diff']

    #drop redundant values
    df2_mod.drop(df2_mod[df2_mod.match_index.str.len()>0].index, inplace=True)

    #merge the two
    df_final = pd.merge(df1_mod, df2_mod, how='outer')

Это дает следующий результат:

In [349]: df_final
Out[349]:
   col1   col2 col3       date    diff match_index
0  1232    asd    1 2010-01-23     NaT         NaN
1   432  dsa12    2 2016-05-20 -3 days          b2
2   432  dsa12    2 2010-06-20 10 days          b3
3   123   asd2    3 2008-10-21     NaT         NaN
4   132    asd    1 2010-01-23     NaT         NaN
5   123    sd2    3 2008-10-21     NaT         NaN

С помощью команды слияния:

In [208]: pd.merge(df1_mod, df2.drop('date', axis=1), on=['col1', 'col2', 'col3']).drop_duplicates()
Out[208]:
  col1   col2 col3       date    diff match_index
0  432  dsa12    2 2016-05-20 -3 days          b2
2  432  dsa12    2 2010-06-20 10 days          b3

Случай рассмотренный в комментариях, а именно:

df1 = pd.DataFrame({'index': ['a1','a2','a3','a4'], 'col1': ['1232','1432','432','123'], 'col2': ['asd','dsa12','dsa12','asd2'], 'col3': ['1','2','2','3'], 'date': ['2010-01-23','2016-05-20','2010-06-20','2008-10-21'],}).set_index('index')

дает следующее:

In [351]: df_final
Out[351]:
   col1   col2 col3       date    diff match_index
0  1232    asd    1 2010-01-23     NaT         NaN
1  1432  dsa12    2 2016-05-20     NaT         NaN
2   432  dsa12    2 2010-06-20 10 days          b3
3   123   asd2    3 2008-10-21     NaT         NaN
4   132    asd    1 2010-01-23     NaT         NaN
5   123    sd2    3 2008-10-21     NaT         NaN

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы