【问题标题】:Pandas - Creating multiple default columns from existing columnPandas - 从现有列创建多个默认列
【发布时间】:2017-12-22 08:18:12
【问题描述】:

以下来自: 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 array-broadcasting


    【解决方案1】:

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

    【讨论】:

    • 谢谢 - 这很有效 - 布尔索引的形状必须与返回的项目匹配。但是......我仍然不确定为什么我们不需要在原始情况下堆叠,即为什么 (df.buyer_name,df.seller_name) 是可广播的,而 ("John","Maggie") 不是?为什么 .T 用来转置原始答案 - 我怀疑这是魔法的一部分?
    • 我对其进行了调查,并使用 N, 1 布尔数组找到了更好的解决方案。请检查一下。
    【解决方案2】:

    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,) - 这是不行的,因为不相等的维度,例如 n2 - 元组的本质是它们是 (2,) 并且与 (n ,) 的买卖。

    最后的情况是

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

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

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

    左边是填充

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

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

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

    【讨论】:

      猜你喜欢
      • 2021-05-14
      • 1970-01-01
      • 2018-04-04
      • 2021-12-22
      • 2017-12-19
      • 2018-12-31
      • 2017-01-16
      • 1970-01-01
      • 2012-10-08
      相关资源
      最近更新 更多