在Pandas文档中,“broadcasting”一词是什么意思?

53
我正在阅读Pandas文档,术语"broadcasting"被广泛地使用, 但从未被定义或解释过。
它是什么意思?
2个回答

72

因此,术语broadcasting来自于NumPy。简而言之,它解释了在n维数组(可以是面板,数据框或序列)或标量值之间执行操作时将产生的输出规则。

使用标量值进行广播

因此,最简单的情况就是乘以标量值:

In [4]:
s = pd.Series(np.arange(5))
s

Out[4]:
0    0
1    1
2    2
3    3
4    4
dtype: int32

In [5]:    
s * 10

Out[5]:
0     0
1    10
2    20
3    30
4    40
dtype: int32

我们使用DataFrame获得相同的预期结果:

In [6]:    
df = pd.DataFrame({'a':np.random.randn(4), 'b':np.random.randn(4)})
df

Out[6]:
          a         b
0  0.216920  0.652193
1  0.968969  0.033369
2  0.637784  0.856836
3 -2.303556  0.426238

In [7]:    
df * 10

Out[7]:
           a         b
0   2.169204  6.521925
1   9.689690  0.333695
2   6.377839  8.568362
3 -23.035557  4.262381

技术上所发生的是标量值已经被沿着以上Series和DataFrame的相同维度进行广播

使用1-D数组进行广播

假设我们有一个形状为 4 x 3 (4行 x 3列) 的二维数据框,我们可以通过使用一个长度与行长度相同的 1-D Series 沿着x轴执行操作:

In [8]:
df = pd.DataFrame({'a':np.random.randn(4), 'b':np.random.randn(4), 'c':np.random.randn(4)})
df

Out[8]:
          a         b         c
0  0.122073 -1.178127 -1.531254
1  0.011346 -0.747583 -1.967079
2 -0.019716 -0.235676  1.419547
3  0.215847  1.112350  0.659432

In [26]:    
df.iloc[0]

Out[26]:
a    0.122073
b   -1.178127
c   -1.531254
Name: 0, dtype: float64

In [27]:    
df + df.iloc[0]

Out[27]:
          a         b         c
0  0.244146 -2.356254 -3.062507
1  0.133419 -1.925710 -3.498333
2  0.102357 -1.413803 -0.111707
3  0.337920 -0.065777 -0.871822

刚开始看上去可能有点滑稽,直到你理解发生了什么,我将第一行数值相加,并逐行添加到 df 中,可以使用这张图片进行可视化(来源于 scipy):

enter image description here

广播的一般规则是:

在一个操作中,两个数组的尾随轴的大小必须相同或其中之一必须为一,才能进行广播。

因此,如果我尝试添加一个长度不匹配的 1-D 数组,比如说一个有 4 个元素的数组,与 numpy 报错不同的是,在 Pandas 中你会得到一个充满 NaN 值的 df:

In [30]:
df + pd.Series(np.arange(4))

Out[30]:
    a   b   c   0   1   2   3
0 NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN

现在,关于pandas的一些优点是它会尝试使用现有的列名和行标签进行对齐,这可能会妨碍像这样执行更高级的广播:

enter image description here

In [55]:
df[['a']] + df.iloc[0]

Out[55]:
          a   b   c
0  0.244146 NaN NaN
1  0.133419 NaN NaN
2  0.102357 NaN NaN
3  0.337920 NaN NaN
在上述内容中,我使用了双下标强制形状为(4,1),但是当尝试使用第一行作为列对齐时会遇到问题,因为只有第一列对齐。要获得与上面图示相同的广播形式,我们必须将其分解为numpy数组,然后这些数组成为匿名数据。
In [56]:
df[['a']].values + df.iloc[0].values

Out[56]:
array([[ 0.24414608, -1.05605392, -1.4091805 ],
       [ 0.13341899, -1.166781  , -1.51990758],
       [ 0.10235701, -1.19784299, -1.55096957],
       [ 0.33792013, -0.96227987, -1.31540645]])

也有可能进行三维广播,但我很少接触那方面的东西,不过numpy、scipy和pandas书中有例子展示如何使用。

一般来说,需要记住的是,除了标量值(简单值)之外,对于n-D数组,次要/尾部轴的长度必须匹配或其中一个长度必须为1。

更新

在最新版本的pandas 0.20.2 中,上述方法似乎会导致ValueError: Unable to coerce to Series, length must be 1: given 3

因此您需要先在df上调用.values

In[42]:
df[['a']].values + df.iloc[0].values

Out[42]: 
array([[ 0.244146, -1.056054, -1.409181],
       [ 0.133419, -1.166781, -1.519908],
       [ 0.102357, -1.197843, -1.55097 ],
       [ 0.33792 , -0.96228 , -1.315407]])

要将其恢复为原始的数据帧,我们可以从np数组构建一个数据帧,并在构造函数的参数中传递原始列:

要将其恢复为原始的df,我们可以从np数组构建一个df,并在构造函数的args中传递原始列:

In[43]:
pd.DataFrame(df[['a']].values + df.iloc[0].values, columns=df.columns)

Out[43]: 
          a         b         c
0  0.244146 -1.056054 -1.409181
1  0.133419 -1.166781 -1.519908
2  0.102357 -1.197843 -1.550970
3  0.337920 -0.962280 -1.315407

3

使用Pandas DataFrames和MultiIndex进行广播

在具有 pandas.MultiIndexDataFrame 中,广播特别有趣,下面我将通过示例向您展示。

通过多维甚至分层索引添加的维度上扩展传播是可能的,如果您知道如何使用它,这非常有用。您不需要编写循环和条件代码,可以依赖已经可用的内容。

我填充了两个 pandas.DataFramesafdf,它们在 0 轴(索引)上具有 pandas.MultiIndex,并且有 10 列标记为整数,例如:蒙特卡罗模拟的场景数据。

afdfpandas.MultiIndex 共享一些公共的 levels(我称之为维度)。不是所有的 labels(较新的 pandas 版本称其为 codes)都需要在匹配的维度中。在示例中,“a”和“c”维度是共享的。在两个框架中,“a”维度具有条目(labels)['A' and 'B'],而在“c”维度中,框架 afbf 分别具有条目 [0, 1, 2, 3][0, 1, 2]

尽管如此,广播仍然很好用。这意味着在下面的示例中,当将两个框架相乘时,针对每个匹配维度中具有匹配条目的组执行组内乘法。

以下示例显示了广播乘法,但它适用于左右手边的所有二进制操作与 pandas.DataFrames 相关。

一些观察结果

请注意,两个框架都可以具有额外的维度。没有必要使一个名称集合成为另一个名称集合的子集。在此示例中,afbf 框架分别具有 ['a', 'b', 'c']['a', 'c', 'd']

结果涵盖整个空间,如预期:['a', 'b', 'c', 'd']

由于维度“c”在框架 bf 中不具有条目(code)“3”,而在 af 中具有,因此结果使用 NaN 填充了结果块。

请注意,这里使用的是 pandas 1.0.3。 使用多个重叠维度进行广播,在 pandas 版本 0.23.4 中无法工作。

同时在0轴和1轴广播也是可行的。请参见最后两个示例。例如,如果您想仅将afbf [0] .to_frame()相乘,即第一种情况。但它只会应用于同样标记的列(因为广播的意图如此)。

进一步的提示

如果您想将af帧与列向量相乘(有时需要使用额外维度应用一些权重),那么您可以轻松地自己实现它。扩展数据帧到n = af.shape [1]列,然后使用该列进行乘法。查看numpy.tile以了解如何“无需”编码完成此操作。

>>> af
Values    0    1    2    3    4    5    6    7    8    9
a b c
A a 0   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    1   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    2   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    3   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
  b 0   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    1   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    2   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    3   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
  c 0   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    1   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    2   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    3   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
B a 0   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    1   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    2   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    3   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
  b 0   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    1   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    2   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    3   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
  c 0   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    1   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    2   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
    3   2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
>>> bf
Values    0    1    2    3    4    5    6    7    8    9
a c d
A 0 *   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
    #   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
  1 *   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
    #   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
  2 *   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
    #   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
B 0 *   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
    #   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
  1 *   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
    #   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
  2 *   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
    #   3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0  3.0
>>> af * bf
Values       0    1    2    3    4    5    6    7    8    9
a c b d
A 0 a *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    b *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    c *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
  1 a *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    b *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    c *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
  2 a *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    b *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    c *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
  3 a NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN
    b NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN
    c NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN
B 0 a *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    b *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    c *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
  1 a *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    b *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    c *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
  2 a *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    b *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
    c *    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
      #    6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0  6.0
  3 a NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN
    b NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN
    c NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN
>>> af * bf[0] # Raises Error: ValueError: cannot join with no overlapping index names

   # Removed that part

>>> af * bf[0].to_frame()  # works consistently
             0   1   2   3   4   5   6   7   8   9
a c b d                                           
A 0 a *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    b *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    c *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
  1 a *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    b *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    c *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
  2 a *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    b *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    c *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
  3 a NaN  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
    b NaN  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
    c NaN  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
B 0 a *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    b *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    c *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
  1 a *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    b *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    c *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
  2 a *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    b *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
    c *    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
      #    6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
  3 a NaN  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
    b NaN  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
    c NaN  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

>>> cf = bf[0].to_frame() 
>>> cf.columns = [3]
>>> af * cf  # And as expected we can broadcast over the same column labels at the same time
            0   1   2    3   4   5   6   7   8   9
a c b d                                           
A 0 a *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    b *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    c *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
  1 a *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    b *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    c *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
  2 a *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    b *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    c *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
  3 a NaN NaN NaN NaN  NaN NaN NaN NaN NaN NaN NaN
    b NaN NaN NaN NaN  NaN NaN NaN NaN NaN NaN NaN
    c NaN NaN NaN NaN  NaN NaN NaN NaN NaN NaN NaN
B 0 a *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    b *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    c *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
  1 a *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    b *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    c *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
  2 a *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    b *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
    c *   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
      #   NaN NaN NaN  6.0 NaN NaN NaN NaN NaN NaN
  3 a NaN NaN NaN NaN  NaN NaN NaN NaN NaN NaN NaN
    b NaN NaN NaN NaN  NaN NaN NaN NaN NaN NaN NaN
    c NaN NaN NaN NaN  NaN NaN NaN NaN NaN NaN NaN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接