将DataFrame的浮点列与DataFrame的pandas区间列合并

3
我有两个DataFrame,我想要合并它们。
import pandas as pd

df1=pd.DataFrame.from_dict({
'names':['klas','erik','stefan'],
'age':[6,17.5,28] 
})

df2=pd.DataFrame.from_dict(
{'salary':[10,15,45,600],
 'names':['klas','erik','stefan','stefan'],
 'age_range':pd.IntervalIndex.from_tuples([(0,10),(10,20),(20,30),(0,10)])    
})

df1:
    names   age
0   klas    6
1   erik    17.5
2   stefan  28

df2:
  salary names  age_range
0   10   klas   (0, 10]
1   15   erik   (10, 20]
2   45   stefan (20, 30]
3   600  stefan (0, 10]

如果我在姓名列上合并,最后一行的年龄不在区间列((0, 10])中。
m1=df1.merge(df2,on='names',how='left')
print(m1)

    names  age  salary   age_range
0   klas    6     10      (0, 10]
1   erik    17.5  15      (10, 20]
2   stefan  28    45      (20, 30]
3   stefan  28    600     (0, 10]

我真正想做的是根据年龄和年龄范围这两个键/列进行合并。 问题在于年龄是一个包含浮点数的列,而年龄范围是一个包含pandas区间的列。 很遗憾,无法像这样进行合并:
df1.merge(df2,left_on=['age','names'],right_on=['age_range','names'],how='left')

这只会导致一个带有NaN值的薪水列。 要得到我想要的结果,目前我必须根据姓名进行合并,然后执行类似以下操作:
def check_if_age_between(age,age_range):
    return age in age_range

f1=lambda row: check_if_age_between(row['age'],row['age_range'])

m1=m1[m1.apply(f1,axis=1)]

print(m1)


    names  age  salary  age_range
0    klas    6      10   (0, 10]
1    erik   17.5    15   (10, 20]
2  stefan   28      45   (20, 30]

有没有办法使用第一个数据框中浮点列的键和第二个数据框中的pandas区间列进行合并?(编辑:将df1中的年龄值从17替换为17.5,以使其成为浮点列而不是整数列)

conditional_join应该在这里有所帮助。 - undefined
好的,那是一个pandas的函数吗?如果不是的话,能否提供包的名称? - undefined
2个回答

2
conditional_join覆盖了您的使用情况 - 您可以从您的间隔数组中创建临时的开始和结束列:
# pip install pyjanitor
import janitor
import pandas as pd
(df1
.conditional_join(
    df2.assign(start=df2.age_range.array.left, 
               end=df2.age_range.array.right), 
    # column from the left, column from the right, operator
    ('names', 'names', '=='), 
    ('age', 'start', '>='), 
    ('age', 'end', '<='), 
    # columns to return from the right dataframe
    right_columns=['salary','age_range'], 
    # more performance may be possible in numba
    # if you have many duplicated values in the equality join
    use_numba=False,
    # you may force the inequality join to execute first
    # if you know that there are less rows to return
    # compared to the inequality join
    force=False,
    how = 'inner')
) 
    names  age  salary age_range
0    klas    6      10   (0, 10]
1    erik   17      15  (10, 20]
2  stefan   28      45  (20, 30]

另一个选择,如果你希望避免使用任何其他第三方工具,就是使用in操作符,逐行检查。这是一个O(n)的操作,所以我不指望会有太大的性能差异。
out = df1.merge(df2, on='names')
filtered = [a in b for a, b in zip(out.age, out.age_range)]
out.loc[filtered]
    names  age  salary age_range
0    klas    6      10   (0, 10]
1    erik   17      15  (10, 20]
2  stefan   28      45  (20, 30]

一个完全向量化的方法是深入数组并获取左右两端:
out = df1.merge(df2, on='names')
filtered = out.age.between(out.age_range.array.left, 
                           out.age_range.array.right)
out.loc[filtered]
    names  age  salary age_range
0    klas    6      10   (0, 10]
1    erik   17      15  (10, 20]
2  stefan   28      45  (20, 30]

1
非常有用的东西,谢谢。 - undefined

2
如果您的年龄范围在df2中既不重叠也没有间隙,您可以动态定义您的分组。
df1["age_range"] = pd.cut(
    df1["age"],
    bins=[df2["age_range"].iloc[0].left]
    + sorted([e.right for e in df2["age_range"].iloc[1:]]),
)

你可以对第一个数据框应用一个年龄范围,使用pd.cut,然后像这样合并:
df1['age_range'] = pd.cut(df1['age'], bins=[0,10,20,30,40])
df_out = df1.merge(df2, on = ['names', 'age_range'])

df_out

输出:

    names  age age_range  salary
0    klas    6   (0, 10]      10
1    erik   17  (10, 20]      15
2  stefan   28  (20, 30]      45

我可以将浮点数放入离散的容器/区间中,然后进行合并。问题是,我不想手动更新一个包含离散容器的列表。如果某些区间范围发生变化,变得更宽/更紧等等,那该怎么办? - undefined
1
@Niltzable 请查看更新。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接