提问人:Angie 提问时间:3/10/2023 最后编辑:Angie 更新时间:3/11/2023 访问量:93
比较字典以检查它们是否相等,或者它们是否从空变为具有值
Compare dictionaries to check if they are equal or if they go from empty to having a value
问:
我有两个数据帧,我在其中遍历每一行,并为我正在查看的行创建一个字典以相互比较。
我通过做以下操作来做到这一点:
ids = []
for row in range(len(df1)-1):
df1_row = dict(df1.iloc[row])
df2_row = dict(df2.iloc[row])
if df1_row == df2_row:
ids.append(df1_row['ID'])
我正在检查我一次比较的两行是否相等,如果相等,我将该行的 id 附加到列表以在最后返回。
但是,我还想检查以下条件:如果 df2 中的行包含给定键的空字符串,而 df1 中的行包含同一键的值,但其余的键值对在它们之间相等,那么我也想将该 id 附加到列表中。
例如,如果我像这样查看两行
df1_row = {'NAME': 'Kelly', 'AGE': '15', 'CITY': 'London', 'GENDER': 'F', 'ID': 15}
df2_row = {'NAME': 'Kelly', 'AGE': '15', 'CITY': '', 'GENDER': 'F', 'ID': '15'}
然后我想将 ID 15 附加到我的列表中,因为 CITY 从 df2_row 中的 EMPTY 变为df1_row中的值。
如果这对看起来像这样
df1_row = {'NAME': 'Kelly', 'AGE': '15', 'CITY': 'London', 'GENDER':'' 'ID': 15}
df2_row = {'NAME': 'Kelly', 'AGE': '15', 'CITY': '', 'GENDER': 'F', 'ID': '15'}
我不想将 id 15 附加到我的结果列表中,因为即使 CITY 从 EMPTY 变为 df2_row 到 df1_row,GENDER 的值从 df2 到 df1 变为 EMPTY。
(基本上我的检查是:行完全相等,或者它们的值从空到非空(从 df2 到 df1),其余值相等)
我试过了
ids = []
for row in range(len(df1)-1):
df1_row = dict(df1.iloc[row])
df2_row = dict(df2.iloc[row])
if df1_row == df2_row:
ids.append(df1_row['ID'])
else:
for key in df1_row:
if df1_row[key] == df2_row[key] or (df2_row[key] == '' and df1_row[key] != ''):
但是我不确定如何编写第二个条件,以便它只在检查整行后附加 id,而不是只检查当前键值上的条件并在那里附加 id......有没有办法一次检查整行的这个条件/另一种写这个的方法?谢谢!(或者,也许有一种更好的方法可以使用这些条件将同一 ID 的 DataFrame 中的两行相互比较,而不必将行转换为字典进行比较?
测试台
DF1:
名字 | 年龄 | 城市 | 性 | 编号 |
---|---|---|---|---|
凯利 | 15 | 伦敦 | F | 15 |
千斤顶 | 12 | M | 98 | |
乔希 | 30 | 奥斯汀 | M | 12 |
DF2:
名字 | 年龄 | 城市 | 性 | 编号 |
---|---|---|---|---|
凯利 | 15 | F | 15 | |
千斤顶 | 慕尼黑 | M | 98 | |
乔希 | 30 | 奥斯汀 | M | 12 |
我想取回 ID 15 和 12,因为 12 完全匹配,并且在 15 中它完全匹配,或者它在 df2 中有一个列值在 df1 中变为非空。
答:
设置:
# import pandas as pd
## [ can just read tables from your question with: ]
## df1, df2 = pd.read_html('https://stackoverflow.com/questions/75688651')[:2]
df1 = pd.DataFrame([{'NAME': 'Kelly', 'AGE': 15, 'CITY': 'London', 'GENDER': 'F', 'ID': 15}, {'NAME': 'Jack', 'AGE': 12, 'CITY': '', 'GENDER': 'M', 'ID': 98}, {'NAME': 'Josh', 'AGE': 30, 'CITY': 'Austin', 'GENDER': 'M', 'ID': 12}])
df2 = pd.DataFrame([{'NAME': 'Kelly', 'AGE': 15.0, 'CITY': '', 'GENDER': 'F', 'ID': 15}, {'NAME': 'Jack', 'AGE': '', 'CITY': 'Munich', 'GENDER': 'M', 'ID': 98}, {'NAME': 'Josh', 'AGE': 30.0, 'CITY': 'Austin', 'GENDER': 'M', 'ID': 12}])
有没有办法一次检查整行的这个条件/另一种写这个的方法?
您可以将 zip
和 .iterrows
与 for...其他
例如:
ids = []
for (i1,df1_row),(i2,df2_row) in zip(df1.fillna('').iterrows(),df2.fillna('').iterrows()):
for df1_val, df2_val in zip(df1_row, df2_row):
if not (df1_val==df2_val or df2_val==''): break
else: ids.append(df2_row['ID'])
[有了这个,如果有两个不相等的值,其中值不为空(并且 .fillna('')
确保所有值都替换为空字符串),则它会中断;如果它永远不会中断(即,所有值都相等或具有空值),则该块将被执行并将添加到 。for...else
df2
nan
''
df2
else
ID
ids
代替 ,您还可以使用 list comprehension with all
来检查每对值,就像您提到的:for...else
checkPair = lambda df1_val, df2_val: df1_val==df2_val or df2_val==''
ids = [df2_row['ID'] for (i_1, df1_row), (i_2, df2_row) in zip(
df1.fillna('').iterrows(), df2.fillna('').iterrows()
) if all(checkPair(v1, v2) for v1, v2 in zip(df1_row, df2_row))]
建议的解决方案:
或者,也许有一种更好的方法可以使用这些条件将同一 ID 的 DataFrame 中的两行相互比较,而不必将行转换为字典进行比较?
是的,pandas 有 .compare
方法,我认为这对于这种情况非常方便。
comp_df = df1.set_index('ID').compare(df2.set_index('ID'), keep_shape=True)
从比较示例中可以看出(比较):df1
df2
self
和列将 [分别] 包含 和 值other
df1
df2
- 如果两者具有相同的值,并且都将包含
df1
df2
self
other
nan
- (除非您设置
keep_equal=True
)
- (除非您设置
- 因此,如果你用 填充 s ,你只需要检查值
nan
''
other
- (查看
comp_df.fillna(''
) 进行演示)
- (查看
因此,使用列表推导式和任何
:
ids = [i for i, r in comp_df.fillna('').iterrows() if not any(list(r!='')[1::2])]
或与:for...else
comp_df = df1.set_index('ID').compare(df2.set_index('ID'), keep_shape=True)
colNames = {c[0] for c in comp_df.columns} # --> {'GENDER', 'AGE', 'CITY', 'NAME'}
ids = []
for i, row in comp_df.fillna('').iterrows():
for c in colNames:
if row[c]['other'] != '': break
else: ids.append(i)
上述 4 种方法中的任何一种都应该返回 。id
[15, 12]
请注意,.compare
要求两个 DataFrame 具有完全相同的形状和标签 [对于列和索引]。[除非所有列和行也以相同的顺序排序,否则前半部分的方法将无法可靠地工作(例如:你不能让 Jack 先进入 Kelly,因为 Kelly 排在第一位)。for...zip...
df2
df1
评论
has a column value in df2 that goes to non empty in df1
- 任何一列?如果 df2 中有 2 个空列,但两个列在 df1 中都有值,它是否符合您的要求?你为什么选择先把每一行变成一个字典?系列是字典式的。缺失值是空字符串还是 null 或 nan?