如何根据另一个 df 的条件填充 pandas df?

How to populate a pandas df based on conditions from another df?

提问人:Rishav Ganguly 提问时间:10/20/2023 最后编辑:Rishav Ganguly 更新时间:10/20/2023 访问量:46

问:

我考虑了 2 个表:

表 1:

国家 年龄 标志着
一个 25 7
B 45 8

表2:

年龄范围从 年龄范围 至 A国 B国
20 30
40 50

我想要的输出:

年龄范围从 年龄范围 至 A国 B国
20 30 7
40 50 8

这是我尝试过的:

for index,row in table2.iterrows():
    table2.loc[index,'Country A'] = table1[(table1['Country']=='A')&
                                           (table1['Age']>=row[0])&
                                           (table1['Age']<=row[1])]['Marks'].values[0]

但这会给出以下错误:index 0 is out of bounds for axis 0 with size 0

我想我可能已经猜到了错误发生的位置: 每当编译器遇到表 2 中的年龄范围时,而表 1 中不存在相应的年龄。

非常感谢对此问题的任何帮助!先谢谢你...

Pandas 操作 数据过滤

评论


答:

0赞 jezrael 10/20/2023 #1

DataFrame.mergeDataFrame.pivot 透视一起使用,并按 Series.between 筛选值:df1

out = (df2[['Age Band From','Age Band To']]
            .merge(df1.pivot(index='Age', columns='Country', values='Marks')
                       .add_prefix('Country ').reset_index(), how='cross'))

out = out[out['Age'].between(out['Age Band From'], out['Age Band To'])]
print (out)
   Age Band From  Age Band To  Age  Country A  Country B
0             20           30   25        7.0        NaN
3             40           50   45        NaN        8.0

对于更通用的解决方案是添加 DataFrame.join

print (df2)
   Age Band From  Age Band To  Country A  Country B
0             20           30        NaN        NaN
1              4            5        NaN        NaN
2             20           35        NaN        NaN
3             40           50        NaN        NaN

cols = ['Age Band From','Age Band To']

out = (df2.reset_index()[cols + ['index']]
            .merge(df1.pivot(index='Age', columns='Country', values='Marks')
                       .add_prefix('Country ').reset_index(), how='cross'))

out = df2[cols].join(out[out['Age'].between(out['Age Band From'], out['Age Band To'])]
                       .set_index('index').rename_axis(None).filter(like='Country'))
print (out)
   Age Band From  Age Band To  Country A  Country B
0             20           30        7.0        NaN
1              4            5        NaN        NaN
2             20           35        7.0        NaN
3             40           50        NaN        8.0
1赞 mozway 10/20/2023 #2

pandas 中没有有效的内置解决方案以通用方式执行此操作。交叉合并适用于小型数据帧,但在大型数据集上的效率很差,甚至很糟糕。事实上,由于它具有二次复杂度,这甚至会导致超过几千行的任何内容都会导致 python 崩溃。

一个可靠的选择是使用 pyjanitor 进行透视然后执行conditional_join

# pip install pyjanitor
import janitor

out = (table2[['Age Band From', 'Age Band To']]
       .conditional_join(table1.pivot(index='Age', columns='Country', values='Marks')
                               .add_prefix('Country ').reset_index(),
                         ('Age Band From', 'Age', '<='),
                         ('Age Band To', 'Age', '>='),
                         how='left'
                        )
      )

输出:

   Age Band From  Age Band To  Age  Country A  Country B
0             20           30   25        7.0        NaN
1             40           50   45        NaN        8.0

效率比较

对包含随机数据(和重复数据删除)的两个表使用长度:N

enter image description here

或者,如果您只有一对一的映射(即没有重叠的间隔,table2 的每行在 table1 中只有一个匹配项),则可以将 merge_asof 用于纯 pandas 解决方案:

tmp = (table1.pivot(index='Age', columns='Country', values='Marks')
             .add_prefix('Country ').reset_index()
             .sort_values(by='Age')
      )

         # merge on left boundary
out = (pd.merge_asof(table2[['Age Band From', 'Age Band To']].reset_index()
                     .sort_values(by='Age Band From'),
                     tmp, direction='forward',
                     left_on='Age Band From', right_on='Age')
         .set_index('index').reindex(table2.index)
         # hide based on right boundary
         .where(lambda d: d.pop('Age').le(d['Age Band To']))
         # restore Band data if needed
         .combine_first(table2)
      )

输出:

   Age Band From  Age Band To  Country A  Country B
0             20           30        7.0        NaN
1             40           50        NaN        8.0