有条件的熊猫合并

时间:2020-01-10 11:20:09

标签: python pandas

我正在尝试在熊猫df之间进行条件合并: 我的df看起来像这样:

df1
import numpy as np
import pandas as pd 

data = {'Name':['Tom', 'JJ', 'ABC', 'Tom', 'JJ', 'ABC', 'Tom', 'Tom'], 'Age':[10, 20, 25, 15, 25, 30, 30, 50]} 
df = pd.DataFrame(data)
df.sort_values(['Name'], ascending = True, inplace = True)

data_new = {'Name':['Tom', 'JJ', 'ABC', 'JJ', 'ABC'], 'Start_Age':[24, 18, 24, 25, 29], 'End_Age':[32, 22, 27, 25, 34]} 
df_2 = pd.DataFrame(data_new) 
df_2["Score"] = np.random.randint(1, 100, df_2.shape[0])
df_2.sort_values(['Name'], ascending = True, inplace = True)

我想将df与df 2合并以获得与df中存在的年龄相对应的分数。 以下是我想做的事情:

df_new_2 = pd.merge(df, df_2, how='left',  left_on = ["Name"], right_on = ["Name"])
df_new_2 = df_new_2[(df_new_2['Age']>=df_new_2['Start_Age'])& (df_new_2['Age']<=df_new_2['End_Age']) ]
df_final = df.merge(df_new_2, how = 'left', on=['Name', 'Age'])
df_final[['Name', 'Score']].ffill(axis = 0) 

我的预期输出是:

Name    Age     Score
ABC     25       86
ABC     30       87
JJ      20       59
JJ      25       22
Tom     10       Nan
Tom     15       Nan
Tom     30       98
Tom     50       98

但是,我还有其他问题。...我在哪里错了?

2 个答案:

答案 0 :(得分:0)

您的填写不正确。您首先需要按姓名和年龄排序,以确保顺序正确无误,还需要按姓名分组,因此仅考虑来自同一人的分数。否则,向前填充将采用任何人的先前得分:

df_final = df_final.sort_values(['Name', 'Age'])
df_final['Score'] = df_final.groupby('Name').ffill()['Score']

这是解决该问题的另一种方法。 它使用助手功能来查找分数。 然后在每行上使用辅助函数来获取姓名和年龄的分数。

def get_score(name, age):
    score = df_2.loc[(df_2.Name == name) & 
                     (df_2.Start_Age <= age) & 
                     (df_2.End_Age >= age)]['Score'].values
    return score[0] if len(score) >= 1 else np.NaN

# user helper function for each row
df['Score'] = df.apply(lambda x: get_score(x.Name, x.Age), axis=1)

您仍然可以像下面这样进行向前填充:

df = df.sort_values(['Name', 'Age'])
df['Score'] = df.groupby('Name').ffill()['Score']

答案 1 :(得分:0)

这是我的解决方案,基于使用np.where()创建过滤器,然后使用输出创建新的数据框。此外,为了避免列名相同,我在Name中更改了列df_2的名称。 df_2 = pd.DataFrame(data_new).rename(columns={'Name':'Name_new'})。除此之外,这是我的代码:

Age = df['Age'].values
e_age = df_2['End_Age'].values
s_age = df_2['Start_Age'].values

i, j = np.where((Age[:, None] >= s_age) & (Age[:, None] <= e_age))

final_df = pd.DataFrame(
    np.column_stack([df.values[i], df_2.values[j]]),
    columns=df.columns.append(df_2.columns)
)
final_df = final_df[final_df['Name'] == final_df['Name_new']]
df_max = df.merge(final_df,how='left')
df_max['Score'] = df_max.groupby('Name').ffill()['Score']
df_max = df_max[['Name','Age','Score']]

输出:

  Name Age Score
0  ABC  25    41
1  ABC  30    46
2   JJ  20    39
3   JJ  25    96
4  Tom  10   NaN
5  Tom  15   NaN
6  Tom  30    78
7  Tom  50    78