2016-08-06 7 views
2

電子メール、件名、タイムスタンプの3つの列に2つのデータフレームをマージします。 データフレーム間のタイムスタンプが異なるため、電子メールグループ&のグループに最も近いタイムスタンプを特定する必要があります。pandasは最も近いタイムスタンプにデータフレームをマージします

以下は、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分05秒であるのに対し[email protected]最も近いマッチタイムスタンプは、10時17分39秒であることに注意してください。

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 

結果は、アカウントのメール&対象になりませんので、最寄りの日付が間違っている主な理由は、間違っています。

期待される結果は参考になる指定したメールや被写体に最も近いtimesstampsを与える機能を改正

enter image description here

です。

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

ただし、グループオブジェクトに対して関数が定義されていないため、エラーが発生します。 これを行う最善の方法は何ですか?

+1

コードやデータのためのPNG形式を使用していけないしてください。 – Merlin

+0

OK、代わりにどのような形式を使用しますか? – TinaW

+0

予想される出力はテキストです。イメージとしてではなくテキストとして投稿に追加します。 –

答えて

3

お知らせの各グループに最も近いタイムスタンプロジックを適用したいです可能関連タイムスタンプペアリング:

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 

あなたは今、タイムスタンプFの差の絶対値を取ることができます各列:次いで

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

['timestamp','email','subject']に基づいて、各グループの最小の差を持つ行を見つけるために

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

を使用します。


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],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

はあなたがemailsubjectdf1df2をマージする場合、その結果 がすべて持っていることを「メール」と「件名」

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

関連する問題