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.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 - 元组的性质是它们是 (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()
.
之前不需要转置
以下来自:
但这本身就是一个不同的问题。应该更简单!
在引用的问题中,讨论了以下一行用于从其他 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
:
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.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 - 元组的性质是它们是 (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()
.