以下内容: 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个新列的单遍解决方案,这些列填充了不同的,固定的默认值,但都取决于另一列中的单个(布尔值)。
关于我错过的内容的任何指示?
答案 0 :(得分: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]]
答案 1 :(得分:0)
jezrael的回答给出了一个非常好的方法。但为了解释为什么只有第一个例子在原始问题中起作用,我发现下面的链接是一个有用的参考:
https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html和 https://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,)并且与(与...冲突) 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被给予额外的维度而不是文本输出 - 但实现了相同的目标 - 保持不匹配&gt ;不同轴上的1个尺寸。
在这种情况下,buy_sell变为(n,1)所以我们有
(n,1)(2,)(2,)
哪个被填充到
(n,1)(1,2)(1,2)
给出(n,2)的广播对象。
这个解决方案的好处是在应用zip()
之前不需要转置。