【问题标题】:How to broadcast and assign a series of values across all columns in a Pandas dataframe?如何在 Pandas 数据框中的所有列中广播和分配一系列值?
【发布时间】:2019-02-11 05:58:11
【问题描述】:

我知道这一定很容易,但我无法弄清楚或找到关于此的现有答案......

假设我有这个数据框...

>>> import pandas as pd
>>> import numpy as np
>>> dates = pd.date_range('20130101', periods=6)
>>> df = pd.DataFrame(np.nan, index=dates, columns=list('ABCD'))
>>> df
             A   B   C   D
2013-01-01 NaN NaN NaN NaN
2013-01-02 NaN NaN NaN NaN
2013-01-03 NaN NaN NaN NaN
2013-01-04 NaN NaN NaN NaN
2013-01-05 NaN NaN NaN NaN
2013-01-06 NaN NaN NaN NaN

设置一个系列的值很容易...

>>> df.loc[:, 'A'] = pd.Series([1,2,3,4,5,6], index=dates)
>>> df
            A   B   C   D
2013-01-01  1 NaN NaN NaN
2013-01-02  2 NaN NaN NaN
2013-01-03  3 NaN NaN NaN
2013-01-04  4 NaN NaN NaN
2013-01-05  5 NaN NaN NaN
2013-01-06  6 NaN NaN NaN

但是如何使用广播设置所有列的值?

>>> default_values = pd.Series([1,2,3,4,5,6], index=dates)
>>> df.loc[:, :] = default_values
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/billtubbs/anaconda/envs/py36/lib/python3.6/site-packages/pandas/core/indexing.py", line 189, in __setitem__
    self._setitem_with_indexer(indexer, value)
  File "/Users/billtubbs/anaconda/envs/py36/lib/python3.6/site-packages/pandas/core/indexing.py", line 651, in _setitem_with_indexer
    value=value)
  File "/Users/billtubbs/anaconda/envs/py36/lib/python3.6/site-packages/pandas/core/internals.py", line 3693, in setitem
    return self.apply('setitem', **kwargs)
  File "/Users/billtubbs/anaconda/envs/py36/lib/python3.6/site-packages/pandas/core/internals.py", line 3581, in apply
    applied = getattr(b, f)(**kwargs)
  File "/Users/billtubbs/anaconda/envs/py36/lib/python3.6/site-packages/pandas/core/internals.py", line 940, in setitem
    values[indexer] = value
ValueError: could not broadcast input array from shape (6) into shape (6,4)

除了这些方式:

>>> for s in df:
...     df.loc[:, s] = default_values
... 

或者:

>>> df.loc[:, :] = np.vstack([default_values]*4).T

更新:

或者:

>>> df.loc[:, :] = default_values.values.reshape(6,1)

【问题讨论】:

    标签: python pandas dataframe assign broadcasting


    【解决方案1】:

    你可以用 NumPy 解决这个问题:

    nvalues = 6
    ncolumns = 4
    default_values = np.repeat(np.arange(nvalues), ncolumns).reshape(nvalues, ncolumns)
    
    df.loc[:, :] = default_values
    

    但是,这并不能满足您在 Pandas 方面进行广播的希望。我不知道有什么技巧可以做到这一点。

    【讨论】:

    • 谢谢,这类似于问题中的第二个解决方案。我简直不敢相信广播在熊猫中不起作用......
    • 只使用 numpy 的缺点是default_values 的顺序可能不一样,因此最好使用 default_values 的索引。
    • @Bill 我想我不明白。 “顺序可能不一样”是什么意思?
    • 说如果 default_values 是一个已经被反转(降序排序)的系列,那么 pandas 仍然会通过首先匹配索引值将值分配给正确的行。而 numpy '盲目地'按给定的顺序粘贴值。
    • @Bill 我明白了。感谢您花时间解释。
    【解决方案2】:

    使用 numpy broadcasting

    s =  pd.Series([1,2,3,4,5,6], index=dates)
    df.loc[:,:] = s.values[:,None]
    

    使用索引匹配

    df.loc[:] = pd.concat([s]*df.columns.size, axis=1)
    

    【讨论】:

    • 好了!
    • 谢谢。这类似于我添加到问题中的附加解决方案。根据我对@Andrey Portnoy 的回答的评论,使用numpy 数组的危险在于default_values 的索引与df.index 不同。
    • @Bill 那么你可以制作一个框架并直接分配它,请参阅编辑
    • 宾果游戏!而已。非常感谢。
    【解决方案3】:

    Pandas 中已经提供了最直接的方法:调用.add 方法并指定要添加新值的方向(轴)。

    In [7]: df.fillna(0).add(default_values, axis=0)
    Out[7]:
                  A    B    C    D
    2013-01-01  1.0  1.0  1.0  1.0
    2013-01-02  2.0  2.0  2.0  2.0
    2013-01-03  3.0  3.0  3.0  3.0
    2013-01-04  4.0  4.0  4.0  4.0
    2013-01-05  5.0  5.0  5.0  5.0
    2013-01-06  6.0  6.0  6.0  6.0
    

    注意:在较新的pandas versions 中,你可以只做df.add(default_values, axis=0, fill_value=0),基本上是为了避免链式方法的语法改进。

    请注意,如果 pandas 的索引对齐思想在这里适用:考虑这种情况,新值仅覆盖目标数据帧的 5 行中的 4 行

    In [37]: default_values = pd.Series([1,2,3,4], index=['a', 'b', 'c', 'd'])
    
    In [38]: df = pd.DataFrame(np.ones(shape=(5,5)) + np.nan, index=['a', 'b', 'c', 'd', 'e'])
    
    In [39]: df.fillna(0).add(default_values, axis=0)
    Out[39]:
         0    1    2    3    4
    a  1.0  1.0  1.0  1.0  1.0
    b  2.0  2.0  2.0  2.0  2.0
    c  3.0  3.0  3.0  3.0  3.0
    d  4.0  4.0  4.0  4.0  4.0
    e  NaN  NaN  NaN  NaN  NaN
    

    在新值系列中找不到的行e变成NaN

    【讨论】:

    • 这是有道理的,但我认为 OP 想要“覆盖值”。这种情况仅适用于NaNs,其他值将获得old_values+new_values。但无论如何都是好方法:)
    • 那很好。在大多数情况下都有效 - 例如,当值是可添加的。一个例外是 default_values 包含的索引值不在 df 中。然后你会得到不想要的NaNs!
    • @Bill,非常好的观点。在我看来,这是故意的,并且遵循 pandas 中的一般索引对齐设计。我将很快添加一个示例。
    【解决方案4】:

    我来到这里寻找一种解决方案,既可以创建新列,又可以为每列(而不是每行)分配一个默认值。虽然这并不是 OP 所要求的,但我发现这个解决方案效果很好。如果合适,请对此发表评论并重定向到特定线程:

    dates = pd.date_range('20130101', periods=6)
    df = pd.DataFrame(np.nan, index=dates, columns=list('ABCD'))
    default_values = pd.Series([1,2,3,4], index=['A','B','C','D'] ).to_dict()
    df = df.assign( **default_values )   # note use of ** notation (kwargs)
    In [97]: df                                                                                                                                      
    Out[97]: 
                A  B  C  D
    2013-01-01  1  2  3  4
    2013-01-02  1  2  3  4
    2013-01-03  1  2  3  4
    2013-01-04  1  2  3  4
    2013-01-05  1  2  3  4
    2013-01-06  1  2  3  4
    

    【讨论】:

    • 非常感谢您!这正是我所需要的!
    猜你喜欢
    • 1970-01-01
    • 2018-09-09
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    • 2019-04-15
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    相关资源
    最近更新 更多