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

Pandas - Creating multiple default columns from existing 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 列?

这个 link 也显示了一些希望: Changing certain values in multiple columns of a pandas DataFrame at once

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

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

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

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

关于我遗漏的任何指示?

我认为您需要将掩码扩展到 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]]

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 个维度,其中第 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 被赋予了额外的维度而不是文本输出 - 但是实现了相同的 objective - 在不同的轴上保持不匹配 >1 个维度。

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

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

剩下的填充到

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

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

这个解决方案的好处是在应用 zip().

之前不需要转置