2016-05-31 2 views
2

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

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

The dataframes выглядеть примерно так:

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 
+0

Это может иметь смысл задавать отдельные вопросы для каждой из вещей, которые вы ищете. – fmarc

ответ

2

Hej mate,

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

импорт панды как э.р. из sklearn.neighbors импорта 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 
+0

Спасибо, Джин! Это _almost_ it, но оно соответствует только одному столбцу 'col3', пока я хочу совместить все команды 'col1',' col2' и 'col3'. Я попытался объединить их в один, используя 'df1 ['matchCol'] = df1 ['col1']. Astype (str) + df1 ['col2']. Astype (str) + df1 ['col3']. Astype (str) 'но это не было бы ... похоже, что оно работает только тогда, когда точное совпадение является числовым значением, а не строкой? –

+0

жаль, что это не имеет ничего общего с числовыми vs строками ... все еще не уверен, почему это не сработает, однако он дает массив ValueError: Found с 0 образцами (shape = (0, 1)), а требуется минимум 1. –

+0

АВТОМОБИЛЬ ERL, я не совсем понимаю. Если возможно, можете ли вы предоставить код, который вы используете, и вы используете вышеупомянутое сообщение об ошибке? –

Смежные вопросы