Pandas - 从现有列创建多个默认列

问题描述 投票:1回答:2

以下是:Pandas - creating 2 new columns based on 2 columns and a separate test column

但这本身就是一个不同的问题。它应该更简单!

在引用的问题中,讨论了以下单行,用于填充来自其他2列的2个新列的数据,并且取决于第三列的值:

df['Buyer ID'], df['Seller ID'] = zip(
    *np.where(df.buy_sell == 'Buy',
             (df.buyer_name,df.seller_name), 
             (df.seller_name,df.buyer_name)).T)

这很好用 - 但是当我尝试简化它以使用固定的标量值而不是其他列中的相应值时,它不起作用。

例如,如果我只有一个可能的买家,John和一个可能的卖家Maggie,那么以下简单的构造就足够了:

df['Buyer ID'], df['Seller ID'] = zip(
    *np.where(df.buy_sell == 'Buy',
             ("John","Maggie"), 
             ("Maggie","John")).T)

这是在内部np.where()调用失败:

operands could not be broadcast together with shapes

我尝试了一些变体,比如在zip()中包装元组,这会改变形状,但我仍然得到错误。我认为问题是(“John”,“Maggie”)不会作为单个列的内容返回。元组扩展到> 1列?

这个链接也显示了一些承诺:Changing certain values in multiple columns of a pandas DataFrame at once

但我认为该解决方案假设您希望编辑的列已经存在,并且您只希望在每列中放置相同的单个值。

我可以通过多次传球解决问题,但这并不理想:

np.where(df.buy_sell == 'Buy', 'John', 'Maggie') 

理想情况下,对于每一行,我想要一个可以扩展到N个新列的单遍解决方案,这些列填充了不同的,固定的默认值,但都取决于另一列中的单个(布尔值)。

关于我缺少什么的任何指示?

python pandas dataframe numpy-broadcasting
2个回答
3
投票

我认为你需要扩展掩码到2d数组,因为numpy.column_stack需要2个新列:

df = pd.DataFrame({'buy_sell': ['Buy','Buy','Buy','Sell','Sell']})

m = df.buy_sell == 'Buy'
mask = np.column_stack([m] * 2)
df1 = pd.DataFrame(np.where(mask, ("John","Maggie"), ("Maggie","John")))
df[['Buyer ID', 'Seller ID']] = df1
print (df)
  buy_sell Buyer ID Seller ID
0      Buy     John    Maggie
1      Buy     John    Maggie
2      Buy     John    Maggie
3     Sell   Maggie      John
4     Sell   Maggie      John

编辑:

经过调查原始解决方案可能是广播布尔掩码,只需要[:, None]用于N x 1数组:

m = df.buy_sell == 'Buy'
df1 = pd.DataFrame(np.where(np.array(m)[:, None], ("John","Maggie"), ("Maggie","John")))
df[['Buyer ID', 'Seller ID']] = df1
print (df)
  buy_sell Buyer ID Seller ID
0      Buy     John    Maggie
1      Buy     John    Maggie
2      Buy     John    Maggie
3     Sell   Maggie      John
4     Sell   Maggie      John

详情:

print (np.array(m)[:, None])

[[ True]
 [ True]
 [ True]
 [False]
 [False]]

0
投票

jezrael的答案给出了一个非常好的方法。但为了解释为什么只有第一个例子在原始问题中起作用,我发现下面的链接是一个有用的参考:

https://docs.scipy.org/doc/numpy/user/basics.broadcasting.htmlhttps://eli.thegreenplace.net/2015/broadcasting-arrays-in-numpy/

我已将这些案例的参考资料应用于手中。

回顾一下:

第一种情况 - 这有效:

np.where(df.buy_sell == 'Buy',(df.buyer_name,df.seller_name),(df.seller_name,df.buyer_name))

第二种情况 - 这不起作用:

np.where(df.buy_sell == 'Buy',("John","Maggie"), ("Maggie","John"))

第三种情况 - 这确实有效:

np.where(df.buy_sell == 'Buy', 'John', 'Maggie') 

在第一种情况下发生了什么(我认为!)是尝试广播:

(n,)(n,)(n,) - 这很好,因为所有非零维度相等

在第二种情况下

(n,)(2,)(2,) - 这不正常,因为不相等的维度,例如n <> 2 - 元组的性质是它们是(2,)并且与(n,)冲突buy_sell。

在最后一种情况下

(n,)(1,)(1,) - 这与上面的相同,但这可行,因为你可以在n上拉伸1,所以它不会发生冲突。

因此,要构造适用于标量情况的东西,我们需要改变元组:

(n,)(2,)(2,)

为避免不匹配,我们将其更改为:

(n,)(2,1)(2,1)

现在这不是很明显,但是numpy会自动做广播这个是左边的(n,)到(1,n),给我们:

(1,n)(2,1)(2,1)

这样,没有不匹配的维度> 1,给出(2,n)的广播对象 - 每行2列n列。你可以通过手动将np.broadcast()应用于3个数组并在结果上调用shape来看到这一点。

了解(x,)和(x,1)之间的区别以了解其工作原理非常重要。基本上 - (x,)只有1维,(x,1)有2个维度,其中第二维被限制为单个值。详情请见:Difference between numpy.array shape (R, 1) and (R,)

因此,使用以下构造可以实现所需的结果:

np.where(df.buy_sell == 'Buy', (["John"],["Maggie"]), (["Maggie"],["John"]))

然后将结果转换为获得n行的2列,因此每行可以作为参数传递到zip()以允许多个赋值。

我很确定jezrael的解决方案有效地做了同样的事情,但在这种情况下,buy_sell被给予额外的维度而不是文本输出 - 但实现了相同的目标 - 在不同的轴上保持不匹配> 1维度。

在这种情况下,buy_sell变为(n,1)所以我们有

(n,1)(2,)(2,)

哪个是填充的

(n,1)(1,2)(1,2)

给出(n,2)的广播对象。

这个解决方案的好处是在应用zip()之前不需要转置。

© www.soinside.com 2019 - 2024. All rights reserved.