我有两个数据框,我在其中循环遍历每一行并为我正在查看的行创建一个字典以相互比较。
我已经这样做了:
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 变为 EMPTY 到 df1。
(基本上我的检查是:行完全相等或者它们的值从空到非空(从 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 的两行相互比较,而不必将这些行转换为字典进行比较?)
测试表
DF1:
姓名 | 年龄 | 城市 | 性别 | ID |
---|---|---|---|---|
凯莉 | 15 | 伦敦 | F | 15 |
杰克 | 12 | 男 | 98 | |
乔希 | 30 | 奥斯汀 | 男 | 12 |
DF2:
姓名 | 年龄 | 城市 | 性别 | ID |
---|---|---|---|---|
凯莉 | 15 | F | 15 | |
杰克 | 慕尼黑 | 男 | 98 | |
乔希 | 30 | 奥斯汀 | 男 | 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...else
一起使用,例如:
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'])
[有了这个
for...else
,如果有两个不相等的值就会中断,其中 df2
值如果不为空(并且 .fillna('')
确保所有 nan
值都被 ''
空字符串替换);如果它永远不会中断(即,所有值都等于或df2
具有空值),将执行else
块并将ID
添加到ids
。]
代替
for...else
,您还可以使用list comprehension和all
来检查您提到的每一对值:
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 的两行相互比较,而不必将行转换为字典进行比较?
.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
)nan
填充''
s,你只需要检查other
值
any
:
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 具有完全相同的形状和标签 [对于列和索引]。同样的顺序(例如:你不能在 for...zip...
中有 Jack 在 Kelly 之前,因为 Kelly 在
df2
中排在第一位)。]