2016-08-06 2 views
2

Я хочу объединить два кадра данных по трем столбцам: адрес электронной почты, тему и временную метку. Временные метки между файлами данных различаются, и поэтому мне нужно определить ближайшую совпадающую временную метку для группы сообщений электронной почты &.pandas merge dataframes на ближайшей временной отметке

Ниже приведен пример воспроизводимости, используя функцию для ближайшего совпадения, предложенную для вопроса this.

import numpy as np 
import pandas as pd 
from pandas.io.parsers import StringIO 

def find_closest_date(timepoint, time_series, add_time_delta_column=True): 
    # takes a pd.Timestamp() instance and a pd.Series with dates in it 
    # calcs the delta between `timepoint` and each date in `time_series` 
    # returns the closest date and optionally the number of days in its time delta 
    deltas = np.abs(time_series - timepoint) 
    idx_closest_date = np.argmin(deltas) 
    res = {"closest_date": time_series.ix[idx_closest_date]} 
    idx = ['closest_date'] 
    if add_time_delta_column: 
     res["closest_delta"] = deltas[idx_closest_date] 
     idx.append('closest_delta') 
    return pd.Series(res, index=idx) 


a = """timestamp,email,subject 
2016-07-01 10:17:00,[email protected],subject3 
2016-07-01 02:01:02,[email protected],welcome 
2016-07-01 14:45:04,[email protected],subject3 
2016-07-01 08:14:02,[email protected],subject2 
2016-07-01 16:26:35,[email protected],subject4 
2016-07-01 10:17:00,[email protected],subject3 
2016-07-01 02:01:02,[email protected],welcome 
2016-07-01 14:45:04,[email protected],subject3 
2016-07-01 08:14:02,[email protected],subject2 
2016-07-01 16:26:35,[email protected],subject4 
""" 

b = """timestamp,email,subject,clicks,var1 
2016-07-01 02:01:14,[email protected],welcome,1,1 
2016-07-01 08:15:48,[email protected],subject2,2,2 
2016-07-01 10:17:39,[email protected],subject3,1,7 
2016-07-01 14:46:01,[email protected],subject3,1,2 
2016-07-01 16:27:28,[email protected],subject4,1,2 
2016-07-01 10:17:05,[email protected],subject3,0,0 
2016-07-01 02:01:03,[email protected],welcome,0,0 
2016-07-01 14:45:05,[email protected],subject3,0,0 
2016-07-01 08:16:00,[email protected],subject2,0,0 
2016-07-01 17:00:00,[email protected],subject4,0,0 
""" 

Обратите внимание, что для [email protected] ближе всего соответствует временная отметка 10:17:39, тогда как для [email protected] ближайший матч 10:17:05.

a = """timestamp,email,subject 
2016-07-01 10:17:00,[email protected],subject3 
2016-07-01 10:17:00,[email protected],subject3 
""" 

b = """timestamp,email,subject,clicks,var1 
2016-07-01 10:17:39,[email protected],subject3,1,7 
2016-07-01 10:17:05,[email protected],subject3,0,0 
""" 
df1 = pd.read_csv(StringIO(a), parse_dates=['timestamp']) 
df2 = pd.read_csv(StringIO(b), parse_dates=['timestamp']) 

df1[['closest', 'time_bt_x_and_y']] = df1.timestamp.apply(find_closest_date, args=[df2.timestamp]) 
df1 

df3 = pd.merge(df1, df2, left_on=['email','subject','closest'], right_on=['email','subject','timestamp'],how='left') 

df3 
timestamp_x  email subject    closest time_bt_x_and_y   timestamp_y clicks var1 
    2016-07-01 10:17:00 [email protected] subject3 2016-07-01 10:17:05   00:00:05     NaT  NaN NaN 
    2016-07-01 02:01:02 [email protected] welcome 2016-07-01 02:01:03   00:00:01     NaT  NaN NaN 
    2016-07-01 14:45:04 [email protected] subject3 2016-07-01 14:45:05   00:00:01     NaT  NaN NaN 
    2016-07-01 08:14:02 [email protected] subject2 2016-07-01 08:15:48   00:01:46 2016-07-01 08:15:48  2.0 2.0 
    2016-07-01 16:26:35 [email protected] subject4 2016-07-01 16:27:28   00:00:53 2016-07-01 16:27:28  1.0 2.0 
    2016-07-01 10:17:00 [email protected] subject3 2016-07-01 10:17:05   00:00:05 2016-07-01 10:17:05  0.0 0.0 
    2016-07-01 02:01:02 [email protected] welcome 2016-07-01 02:01:03   00:00:01 2016-07-01 02:01:03  0.0 0.0 
    2016-07-01 14:45:04 [email protected] subject3 2016-07-01 14:45:05   00:00:01 2016-07-01 14:45:05  0.0 0.0 
    2016-07-01 08:14:02 [email protected] subject2 2016-07-01 08:15:48   00:01:46     NaT  NaN NaN 
    2016-07-01 16:26:35 [email protected] subject4 2016-07-01 16:27:28   00:00:53     NaT  NaN NaN 

В результате неправильно, в основном потому, что ближайшая дата неверна, поскольку она не учитывает электронную почту & предмета.

Ожидаемый результат

enter image description here

внесении изменения в функцию, чтобы дать ближайший timesstamps для данной электронной почты и тему будет полезно.

df1.groupby(['email','subject'])['timestamp'].apply(find_closest_date, args=[df1.timestamp]) 

Но это дает ошибку, поскольку функция не определена для группового объекта. Каков наилучший способ сделать это?

+1

Пожалуйста, не использовать детские для кода или данных. – Merlin

+0

ОК, в каком формате вы хотите? – TinaW

+0

Ваш ожидаемый результат - текст; добавьте его в свой пост как текст, а не как изображение. –

ответ

3

Обратите внимание, что если вы объединяете df1 и df2 на email и subject, то результат имеет все возможные соответствующие временных меток спариваний:

In [108]: result = pd.merge(df1, df2, how='left', on=['email','subject'], suffixes=['', '_y']); result 
Out[108]: 
      timestamp  email subject   timestamp_y clicks var1 
0 2016-07-01 10:17:00 [email protected] subject3 2016-07-01 10:17:39  1  7 
1 2016-07-01 10:17:00 [email protected] subject3 2016-07-01 14:46:01  1  2 
2 2016-07-01 02:01:02 [email protected] welcome 2016-07-01 02:01:14  1  1 
3 2016-07-01 14:45:04 [email protected] subject3 2016-07-01 10:17:39  1  7 
4 2016-07-01 14:45:04 [email protected] subject3 2016-07-01 14:46:01  1  2 
5 2016-07-01 08:14:02 [email protected] subject2 2016-07-01 08:15:48  2  2 
6 2016-07-01 16:26:35 [email protected] subject4 2016-07-01 16:27:28  1  2 
7 2016-07-01 10:17:00 [email protected] subject3 2016-07-01 10:17:05  0  0 
8 2016-07-01 10:17:00 [email protected] subject3 2016-07-01 14:45:05  0  0 
9 2016-07-01 02:01:02 [email protected] welcome 2016-07-01 02:01:03  0  0 
10 2016-07-01 14:45:04 [email protected] subject3 2016-07-01 10:17:05  0  0 
11 2016-07-01 14:45:04 [email protected] subject3 2016-07-01 14:45:05  0  0 
12 2016-07-01 08:14:02 [email protected] subject2 2016-07-01 08:16:00  0  0 
13 2016-07-01 16:26:35 [email protected] subject4 2016-07-01 17:00:00  0  0 

Вы могли теперь взять абсолютное значение разности в метках времени е или каждая строка:

result['diff'] = (result['timestamp_y'] - result['timestamp']).abs() 

, а затем использовать

idx = result.groupby(['timestamp','email','subject'])['diff'].idxmin() 
result = result.loc[idx] 

, чтобы найти строки с минимальной разностью для каждой группы на основе ['timestamp','email','subject'].


import numpy as np 
import pandas as pd 
from pandas.io.parsers import StringIO 

a = """timestamp,email,subject 
2016-07-01 10:17:00,[email protected],subject3 
2016-07-01 02:01:02,[email protected],welcome 
2016-07-01 14:45:04,[email protected],subject3 
2016-07-01 08:14:02,[email protected],subject2 
2016-07-01 16:26:35,[email protected],subject4 
2016-07-01 10:17:00,[email protected],subject3 
2016-07-01 02:01:02,[email protected],welcome 
2016-07-01 14:45:04,[email protected],subject3 
2016-07-01 08:14:02,[email protected],subject2 
2016-07-01 16:26:35,[email protected],subject4 
""" 

b = """timestamp,email,subject,clicks,var1 
2016-07-01 02:01:14,[email protected],welcome,1,1 
2016-07-01 08:15:48,[email protected],subject2,2,2 
2016-07-01 10:17:39,[email protected]ail.com,subject3,1,7 
2016-07-01 14:46:01,[email protected],subject3,1,2 
2016-07-01 16:27:28,[email protected],subject4,1,2 
2016-07-01 10:17:05,[email protected],subject3,0,0 
2016-07-01 02:01:03,[email protected],welcome,0,0 
2016-07-01 14:45:05,[email protected],subject3,0,0 
2016-07-01 08:16:00,[email protected],subject2,0,0 
2016-07-01 17:00:00,[email protected],subject4,0,0 
""" 

df1 = pd.read_csv(StringIO(a), parse_dates=['timestamp']) 
df2 = pd.read_csv(StringIO(b), parse_dates=['timestamp']) 

result = pd.merge(df1, df2, how='left', on=['email','subject'], suffixes=['', '_y']) 
result['diff'] = (result['timestamp_y'] - result['timestamp']).abs() 
idx = result.groupby(['timestamp','email','subject'])['diff'].idxmin() 
result = result.loc[idx].drop(['timestamp_y','diff'], axis=1) 
result = result.sort_index() 
print(result) 

дает

   timestamp  email subject clicks var1 
0 2016-07-01 10:17:00 [email protected] subject3  1  7 
2 2016-07-01 02:01:02 [email protected] welcome  1  1 
4 2016-07-01 14:45:04 [email protected] subject3  1  2 
5 2016-07-01 08:14:02 [email protected] subject2  2  2 
6 2016-07-01 16:26:35 [email protected] subject4  1  2 
7 2016-07-01 10:17:00 [email protected] subject3  0  0 
9 2016-07-01 02:01:02 [email protected] welcome  0  0 
11 2016-07-01 14:45:04 [email protected] subject3  0  0 
12 2016-07-01 08:14:02 [email protected] subject2  0  0 
13 2016-07-01 16:26:35 [email protected] subject4  0  0 
+0

Большое спасибо !!! – TinaW

1

Вы хотите применить ближайшую временную метку логику для каждой группы «электронной почты» и «предмет»

a = """timestamp,email,subject 
2016-07-01 10:17:00,[email protected],subject3 
2016-07-01 02:01:02,[email protected],welcome 
2016-07-01 14:45:04,[email protected],subject3 
2016-07-01 08:14:02,[email protected],subject2 
2016-07-01 16:26:35,[email protected],subject4 
2016-07-01 10:17:00,[email protected],subject3 
2016-07-01 02:01:02,[email protected],welcome 
2016-07-01 14:45:04,[email protected],subject3 
2016-07-01 08:14:02,[email protected],subject2 
2016-07-01 16:26:35,[email protected],subject4 
""" 

b = """timestamp,email,subject,clicks,var1 
2016-07-01 02:01:14,[email protected],welcome,1,1 
2016-07-01 08:15:48,[email protected],subject2,2,2 
2016-07-01 10:17:39,[email protected],subject3,1,7 
2016-07-01 14:46:01,[email protected],subject3,1,2 
2016-07-01 16:27:28,[email protected],subject4,1,2 
2016-07-01 10:17:05,[email protected],subject3,0,0 
2016-07-01 02:01:03,[email protected],welcome,0,0 
2016-07-01 14:45:05,[email protected],subject3,0,0 
2016-07-01 08:16:00,[email protected],subject2,0,0 
2016-07-01 17:00:00,[email protected],subject4,0,0 
""" 

df1 = pd.read_csv(StringIO(a), parse_dates=['timestamp']) 
df2 = pd.read_csv(StringIO(b), parse_dates=['timestamp']) 
df2 = df2.set_index(['email', 'subject']) 

def find_closest_date(timepoint, time_series, add_time_delta_column=True): 
    # takes a pd.Timestamp() instance and a pd.Series with dates in it 
    # calcs the delta between `timepoint` and each date in `time_series` 
    # returns the closest date and optionally the number of days in its time delta 
    time_series = time_series.values 
    timepoint = np.datetime64(timepoint) 
    deltas = np.abs(np.subtract(time_series, timepoint)) 
    idx_closest_date = np.argmin(deltas) 
    res = {"closest_date": time_series[idx_closest_date]} 
    idx = ['closest_date'] 
    if add_time_delta_column: 
     res["closest_delta"] = deltas[idx_closest_date] 
     idx.append('closest_delta') 
    return pd.Series(res, index=idx) 

# Then group df1 as needed 
grouped = df1.groupby(['email', 'subject']) 

# Finally loop over the group items, finding the closest timestamps 
join_ts = pd.DataFrame() 
for name, group in grouped: 
    try: 
     join_ts = pd.concat([join_ts, group['timestamp']\ 
          .apply(find_closest_date, time_series=df2.loc[name, 'timestamp'])], 
          axis=0) 
    except KeyError: 
     pass 

df3 = pd.merge(pd.concat([df1, join_ts], axis=1), df2, left_on=['closest_date'], right_on=['timestamp']) 
+0

К сожалению, это не дает ожидаемого результата. – TinaW

+0

Так что он дает? Ошибка, что-то еще? Можете ли вы быть более конкретным? Пожалуйста. – Kartik

+0

Картинка в моем посте показывает ожидаемый результат. Основная проблема заключается в том, что самая близкая временная метка ошибочна, так как она не учитывает 2 других измерения, которые являются электронной почтой и предметом.Если вы посмотрите на результат своего внутреннего соединения, он содержит только 5 электронных писем, но должен показать 10. (см. Рисунок в моем сообщении). – TinaW

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