根据另一个数据框 python pandas 替换列值 - 更好的方法?

Posted

技术标签:

【中文标题】根据另一个数据框 python pandas 替换列值 - 更好的方法?【英文标题】:Replace column values based on another dataframe python pandas - better way? 【发布时间】:2014-09-06 06:22:39 【问题描述】:

注意:为简单起见,我使用了一个玩具示例,因为在堆栈溢出中复制/粘贴数据帧很困难(如果有简单的方法,请告诉我)。

有没有办法在不获取 _X、_Y 列的情况下将一个数据帧中的值合并到另一个数据帧中?我希望一列上的值替换另一列的所有零值。

df1: 

Name   Nonprofit    Business    Education

X      1             1           0
Y      0             1           0   <- Y and Z have zero values for Nonprofit and Educ
Z      0             0           0
Y      0             1           0

df2:

Name   Nonprofit    Education
Y       1            1     <- this df has the correct values. 
Z       1            1



pd.merge(df1, df2, on='Name', how='outer')

Name   Nonprofit_X    Business    Education_X     Nonprofit_Y     Education_Y
Y       1                1          1                1               1
Y      1                 1          1                1               1
X      1                 1          0               nan             nan   
Z      1                 1          1                1               1

在上一篇文章中,我尝试了 combine_First 和 dropna(),但这些都不起作用。

我想用 df2 中的值替换 df1 中的零。 此外,我希望根据 df2 更改具有相同名称的所有行。

Name    Nonprofit     Business    Education
Y        1             1           1
Y        1             1           1 
X        1             1           0
Z        1             0           1

(需要澄清:name = Z 的“Business”列中的值应该为 0。)

我现有的解决方案执行以下操作: 我根据 df2 中存在的名称进行子集化,然后将这些值替换为正确的值。但是,我想要一种不那么老套的方法来做到这一点。

pubunis_df = df2
sdf = df1 

regex = str_to_regex(', '.join(pubunis_df.ORGS))

pubunis = searchnamesre(sdf, 'ORGS', regex)

sdf.ix[pubunis.index, ['Education', 'Public']] = 1
searchnamesre(sdf, 'ORGS', regex)

【问题讨论】:

我不太明白你的逻辑,你想用另一个 df 的匹配值更新第一个 df,然后你将 Z 的业务值设置为 1,对吗?原来是 0。 【参考方案1】:

注意:在最新版本的熊猫中,以上两个答案都不再起作用了:

KSD 的回答会报错:

df1 = pd.DataFrame([["X",1,1,0],
              ["Y",0,1,0],
              ["Z",0,0,0],
              ["Y",0,0,0]],columns=["Name","Nonprofit","Business", "Education"])    

df2 = pd.DataFrame([["Y",1,1],
              ["Z",1,1]],columns=["Name","Nonprofit", "Education"])   

df1.loc[df1.Name.isin(df2.Name), ['Nonprofit', 'Education']] = df2.loc[df2.Name.isin(df1.Name),['Nonprofit', 'Education']].values

df1.loc[df1.Name.isin(df2.Name), ['Nonprofit', 'Education']] = df2[['Nonprofit', 'Education']].values

Out[851]:
ValueError: shape mismatch: value array of shape (2,) could not be broadcast to indexing result of shape (3,)

而 EdChum 的回答会给我们错误的结果:

 df1.loc[df1.Name.isin(df2.Name), ['Nonprofit', 'Education']] = df2[['Nonprofit', 'Education']]

df1
Out[852]: 
  Name  Nonprofit  Business  Education
0    X        1.0         1        0.0
1    Y        1.0         1        1.0
2    Z        NaN         0        NaN
3    Y        NaN         1        NaN

好吧,只有当“名称”列中的值是唯一的并且在两个数据框中都排序时,它才会安全地工作。

这是我的答案:

方式一:

df1 = df1.merge(df2,on='Name',how="left")
df1['Nonprofit_y'] = df1['Nonprofit_y'].fillna(df1['Nonprofit_x'])
df1['Business_y'] = df1['Business_y'].fillna(df1['Business_x'])
df1.drop(["Business_x","Nonprofit_x"],inplace=True,axis=1)
df1.rename(columns='Business_y':'Business','Nonprofit_y':'Nonprofit',inplace=True)

方式2:

df1 = df1.set_index('Name')
df2 = df2.set_index('Name')
df1.update(df2)
df1.reset_index(inplace=True)

More guide about update.。在“更新”之前,两个数据框需要设置索引的列名不必相同。您可以尝试“Name1”和“Name2”。此外,即使 df2 中的其他不必要的行也不会更新 df1,它也可以工作。换句话说,df2 不需要是 df1 的超集。

例子:

df1 = pd.DataFrame([["X",1,1,0],
              ["Y",0,1,0],
              ["Z",0,0,0],
              ["Y",0,1,0]],columns=["Name1","Nonprofit","Business", "Education"])    

df2 = pd.DataFrame([["Y",1,1],
              ["Z",1,1],
              ['U',1,3]],columns=["Name2","Nonprofit", "Education"])   

df1 = df1.set_index('Name1')
df2 = df2.set_index('Name2')


df1.update(df2)

结果:

      Nonprofit  Business  Education
Name1                                
X           1.0         1        0.0
Y           1.0         1        1.0
Z           1.0         0        1.0
Y           1.0         1        1.0

【讨论】:

这对我帮助很大。像您这样的社区成员回来并为下一批知识寻求者提供最新细节的方式真的非常值得称道。太感谢了!! @杰里米Z 我的荣幸! :) 谢谢@JeremyZ。 方式 1 对我来说是正确的!但我不太明白第二种方式, update() 方法,会更新第一个 DF 的所有数字列吗?您可以使用 update() 选择要更新的特定列吗? @Emiliano 如果您想选择一个特定的列,只需尝试 df1["Education"].update(df2["Education"]) @Jeremy Z 方式 2 不起作用!ValueError: cannot reindex from a duplicate axis【参考方案2】:

使用isin 中的布尔掩码过滤 df 并从 rhs df 中分配所需的行值:

In [27]:

df.loc[df.Name.isin(df1.Name), ['Nonprofit', 'Education']] = df1[['Nonprofit', 'Education']]
df
Out[27]:
  Name  Nonprofit  Business  Education
0    X          1         1          0
1    Y          1         1          1
2    Z          1         0          1
3    Y          1         1          1

[4 rows x 4 columns]

【讨论】:

我看到这给了我错误。看起来下面的答案是真正有效的答案。 这依赖于操作想要的索引匹配,如果索引不匹配,这是一个不同的问题,在这种情况下,其他答案更合适。但是,索引对齐是 pandas 的主要功能之一,因此它取决于用例 您对索引的看法是正确的。为反对票道歉。【参考方案3】:

在 [27] 中: 这是正确的。

df.loc[df.Name.isin(df1.Name), ['Nonprofit', 'Education']] = df1[['Nonprofit', 'Education']].values

df
Out[27]:

Name  Nonprofit  Business  Education

0    X          1         1          0
1    Y          1         1          1
2    Z          1         0          1
3    Y          1         1          1

[4 行 x 4 列]

仅当 df1 中的所有行都存在于 df 中时,上述内容才有效。换句话说 df 应该是 df1 的超集

如果您在 df1 中有一些与 df 不匹配的行,则应遵循以下操作

换句话说 df 不是 df1 的超集:

df.loc[df.Name.isin(df1.Name), ['Nonprofit', 'Education']] = 
df1.loc[df1.Name.isin(df.Name),['Nonprofit', 'Education']].values

【讨论】:

这个答案对我有用,而接受的没有。对于接受的答案,执行 isin 行后值的分配不匹配。 这里也一样。与其他人一起,我在更新字符串时得到了正确的条目和 NaN。有了这个,至少我的更新是正确的。 这对我有用。最后的 .values 帮助 NaN 没有出现最后一条记录。前两个答案无效。【参考方案4】:
df2.set_index('Name').combine_first(df1.set_index('Name')).reset_index()

【讨论】:

以上是关于根据另一个数据框 python pandas 替换列值 - 更好的方法?的主要内容,如果未能解决你的问题,请参考以下文章

Pandas 根据另一个数据框中的匹配列填充新的数据框列

为啥 Python 用后来调整的副本替换早期定义的 Pandas 数据框? [复制]

如何用dict pandas python替换分组数据框

Pandas SettingWithCopyWarning [重复]

根据另一列中的值删除一列的重复项,Python,Pandas

Python Pandas根据多个其他列中的条件替换一列中的值[重复]