基于另一个数据框的条件填充pandas的na值

0 投票
1 回答
81 浏览
提问于 2025-04-14 15:28

表格 1

ID 条件 访问者1 访问者2
1 aa nan nan
2 aa nan nan
3 bb nan nan
4 bb nan nan
5 aa nan nan
6 bb nan nan
7 aa nan nan

表格 2

姓名 条件 访问者1 访问者2
约翰 aa
玛丽 aa
鲍勃 aa
bb
彼得 bb

表格 1 里列出了条件。

表格 2 列出了条件和访问者的姓名,以及他们的访问权限。也就是说,他们可以访问 1、2 或者两者都可以。

举个例子,约翰可以访问 1,但不能访问 2,所以我们需要和表格 1 的条件匹配。ID 1 的条件是 'aa',所以约翰可以访问1,但他的名字没有填在访问2里,然后继续找其他可以访问的人。玛丽的条件是 'aa',她可以访问 2,所以她的名字填在了访问2里。

鲍勃的条件是 'aa',而 ID 2 的条件也是 'aa'。鲍勃可以访问 1 或 2。因此,他的名字填在了 ID 2 的访问1 和访问2里。

ID 5 里,因为鲍勃是最后一个条件为 'aa' 的名字,所以会再次回到约翰。其他的情况也是一样。

结果表

ID 条件 访问者1 访问者2
1 aa 约翰 玛丽
2 aa 鲍勃 鲍勃
3 bb
4 bb 彼得
5 aa 约翰 玛丽
6 bb
7 aa 鲍勃 鲍勃

1 个回答

1

我的想法是利用表2创建一个循环列表,然后填充表1。我的解决方案是结合使用条件语句和pandas的广播概念。

举个例子,我们想用条件aa来填充表1Access1列,

  • 表2中,符合条件aa的名字有三个:[John, Mary, Bob]
  • 但是根据Accessor1列的条件,我们只能用[John, Bob]来填充表1

1. 将表2的Accessor列转换为dtype bool,这样我们就可以使用pandas的布尔索引来创建条件

import numpy as np
import pandas as pd
from itertools import cycle

table_1 = pd.DataFrame(
    {
        "ID": list(range(1, 7)),
        "Condition": ['aa', 'aa', 'bb', 'bb', 'aa', 'bb'],
        "Access1": [np.nan]*6,
        "Access2": [np.nan]*6
    }
)

table_2 = pd.DataFrame(
    {
        "Name": ['John', 'Mary', 'Bob', 'Ben', 'Peter'],
        "Condition": ['aa', 'aa', 'aa', 'bb', 'bb'],
        "Accessor1": ['Yes', 'No', 'Yes', 'Yes', 'No'],
        "Accessor2": ['No', 'Yes', 'Yes', 'Yes', 'Yes']
    }
)

table_2['Accessor1'] = table_2['Accessor1'].apply(lambda x: True if x == 'Yes' else False)
table_2['Accessor2'] = table_2['Accessor2'].apply(lambda x: True if x == 'Yes' else False)

2. 创建可以用来填充列的列表

condition_aa = table_2[table_2['Condition'] == 'aa']
condition_bb = table_2[table_2['Condition'] == 'bb']
condition_aa_access1 = condition_aa['Name'][condition_aa['Accessor1']].to_list()
condition_aa_access2 = condition_aa['Name'][condition_aa['Accessor2']].to_list()
condition_bb_access1 = condition_bb['Name'][condition_bb['Accessor1']].to_list()
condition_bb_access2 = condition_bb['Name'][condition_bb['Accessor2']].to_list()

3. 将列表转换为循环列表,这样我们就可以填充需要更多行的列,而我们手头的行数不够

condition_aa_access1_cycle = cycle(condition_aa_access1)
condition_bb_access1_cycle = cycle(condition_bb_access1)
condition_aa_access2_cycle = cycle(condition_aa_access2)
condition_bb_access2_cycle = cycle(condition_bb_access2)
condition_access1 = []

4. 根据条件创建与列大小相同的列表

for i in range(6):
    if table_1.loc[i, 'Condition'] == 'aa':
        condition_access1.append(next(condition_aa_access1_cycle))
    else:
        condition_access1.append(next(condition_bb_access1_cycle))

condition_access2 = []
for i in range(6):
    if table_1.loc[i, 'Condition'] == 'aa':
        condition_access2.append(next(condition_aa_access2_cycle))
    else:
        condition_access2.append(next(condition_bb_access2_cycle))

5. 最后用上面创建的列表填充空值

table_1.loc[table_1.Access1.isnull(), 'Access1'] = condition_access1
table_1.loc[table_1.Access2.isnull(), 'Access2'] = condition_access2

table_1

结果

 ID Condition Access1 Access2
  1        aa    John    Mary
  2        aa     Bob     Bob
  3        bb     Ben     Ben
  4        bb     Ben   Peter
  5        aa    John    Mary
  6        bb     Ben     Ben

注意:这里有很多地方可以优化,欢迎分享完全依赖于pandas概念的新解决方案。

撰写回答