如何在每个列都有系列的DataFrame上进行操作


问题内容

目标与动机

我已经多次看过这种问题,也看到过许多其他涉及到这一问题的问题。最近,我在寻找适当的规范问答时不得不花一些时间在评论中解释这个概念。我找不到一个,所以我想写一个。

这个问题通常是针对特定的运算出现的,但同样适用于大多数算术运算。

  • 如何从aSeries的每一列中减去a DataFrame
  • 如何Series从中的每一列添加a DataFrame
  • 如何Series从a的每一列乘以a DataFrame
  • 如何Series从a的每一列中划分a DataFrame

问题

给定Series sDataFrame df。如何处理dfwith的每一列s

df = pd.DataFrame(
    [[1, 2, 3], [4, 5, 6]],
    index=[0, 1],
    columns=['a', 'b', 'c']
)

s = pd.Series([3, 14], index=[0, 1])

当我尝试添加它们时,我得到了所有 np.nan

df + s

    a   b   c   0   1
0 NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN

我以为我应该得到的是

    a   b   c
0   4   5   6
1  18  19  20

问题答案:

请承担序言。首先解决一些更高层次的概念很重要。由于我的动机是分享知识和授课,所以我想使这一点尽可能清晰。


它有利于建立一个什么样的心理模型SeriesDataFrame对象。

解剖 Series

Series应该将A视为增强型词典。这并不总是一个完美的类比,但是我们将从这里开始。另外,您还可以进行其他类比,但我将目标放在字典上,以证明本文的目的。

index

这些是我们可以参考以获取相应值的键。当索引的元素是唯一的时,与字典的比较变得非常接近。

values

这些是由索引键入的相应值。

解剖 DataFrame

一个DataFrame应该被看作是一个字典SeriesSeriesSeries。在这种情况下,键是列名称,值是列本身作为Series对象。每个人都Series同意分享相同的内容index,这是的索引DataFrame

columns

这些是我们可以参考以获取相应键的键Series

index

这是所有Series值均同意共享的索引。

注意:RE:columnsindex对象

它们是同一种东西。甲DataFrame小号index可以用作另一DataFrame小号columns。实际上,当您进行df.T移调时会发生这种情况。

values

这是一个二维数组,其中包含数据DataFrame。现实情况是,values不是
有什么存储在里面DataFrame的对象。(有时候是这样,但是我不想描述块管理器)。关键是,最好将其视为对数据二维数组的访问。


定义样本数据

这些样本pandas.Index可以被用作对象index的一个SeriesDataFrame或可以用作所述columns的一DataFrame

idx_lower = pd.Index([*'abcde'], name='lower')
idx_range = pd.RangeIndex(5, name='range')

这些是pandas.Series使用上述pandas.Index对象的示例对象

s0 = pd.Series(range(10, 15), idx_lower)
s1 = pd.Series(range(30, 40, 2), idx_lower)
s2 = pd.Series(range(50, 10, -8), idx_range)

这些是pandas.DataFrame使用上述pandas.Index对象的示例对象

df0 = pd.DataFrame(100, index=idx_range, columns=idx_lower)
df1 = pd.DataFrame(
    np.arange(np.product(df0.shape)).reshape(df0.shape),
    index=idx_range, columns=idx_lower
)

SeriesSeries

当在两个上操作时Series,对齐是显而易见的。您将index其中一个Seriesindex另一个对齐。

s1 + s0

lower
a    40
b    43
c    46
d    49
e    52
dtype: int64

这与我在操作前随机洗牌时的情况相同。索引仍将对齐。

s1 + s0.sample(frac=1)

lower
a    40
b    43
c    46
d    49
e    52
dtype: int64

而且是 不是
时候,而不是我与洗牌的值进行操作的情况Series。在这种情况下,Pandas不需要index与之对齐,因此只能从某个位置操作。

s1 + s0.sample(frac=1).values

lower
a    42
b    42
c    47
d    50
e    49
dtype: int64

添加标量

s1 + 1

lower
a    31
b    33
c    35
d    37
e    39
dtype: int64

DataFrameDataFrame

在两个DataFrames之间进行操作时,类似情况也是如此
。对齐是显而易见的,并且执行了我们认为应该做的事情

df0 + df1

lower    a    b    c    d    e
range                         
0      100  101  102  103  104
1      105  106  107  108  109
2      110  111  112  113  114
3      115  116  117  118  119
4      120  121  122  123  124

DataFrame在两个轴上随机播放秒。在indexcolumns仍然对齐,给我们同样的事情。

df0 + df1.sample(frac=1).sample(frac=1, axis=1)

lower    a    b    c    d    e
range                         
0      100  101  102  103  104
1      105  106  107  108  109
2      110  111  112  113  114
3      115  116  117  118  119
4      120  121  122  123  124

同样的改组,但添加数组而不是DataFrame。不再对齐,将获得不同的结果。

df0 + df1.sample(frac=1).sample(frac=1, axis=1).values

lower    a    b    c    d    e
range                         
0      123  124  121  122  120
1      118  119  116  117  115
2      108  109  106  107  105
3      103  104  101  102  100
4      113  114  111  112  110

添加一维数组。将与列对齐并跨行广播。

df0 + [*range(2, df0.shape[1] + 2)]

lower    a    b    c    d    e
range                         
0      102  103  104  105  106
1      102  103  104  105  106
2      102  103  104  105  106
3      102  103  104  105  106
4      102  103  104  105  106

添加标量。没有什么可以与所有广播内容保持一致的

df0 + 1

lower    a    b    c    d    e
range                         
0      101  101  101  101  101
1      101  101  101  101  101
2      101  101  101  101  101
3      101  101  101  101  101
4      101  101  101  101  101

DataFrameSeries

如果将DataFrames视为值的字典,Series并将sSeries视为值的字典,那么很自然的是,在aDataFrame和之间进行操作时Series,应按“键”对齐它们。

s0:
lower    a    b    c    d    e
        10   11   12   13   14

df0:
lower    a    b    c    d    e
range                         
0      100  100  100  100  100
1      100  100  100  100  100
2      100  100  100  100  100
3      100  100  100  100  100
4      100  100  100  100  100

当我们进行操作时,10ins0['a']被添加到df0['a']

df0 + s0

lower    a    b    c    d    e
range                         
0      110  111  112  113  114
1      110  111  112  113  114
2      110  111  112  113  114
3      110  111  112  113  114
4      110  111  112  113  114

问题的核心和帖子的重点

如果我要s2df0怎么办?

s2:               df0:

             |    lower    a    b    c    d    e
range        |    range                         
0      50    |    0      100  100  100  100  100
1      42    |    1      100  100  100  100  100
2      34    |    2      100  100  100  100  100
3      26    |    3      100  100  100  100  100
4      18    |    4      100  100  100  100  100

当我手术时,我得到np.nan了问题中所引用的一切

df0 + s2

        a   b   c   d   e   0   1   2   3   4
range                                        
0     NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1     NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2     NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3     NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4     NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

这不会产生我们想要的。因为Pandas正在将indexofs2columnsof对齐df0。该columns结果包含的工会indexs2columnsdf0

我们可以通过棘手的换位来伪造它

(df0.T + s2).T

lower    a    b    c    d    e
range                         
0      150  150  150  150  150
1      142  142  142  142  142
2      134  134  134  134  134
3      126  126  126  126  126
4      118  118  118  118  118

但是事实证明,熊猫有更好的解决方案。有一些操作方法可让我们传递axis参数以指定要对齐的轴。

- sub
+ add
* mul
/ div
** pow

所以答案很简单

df0.add(s2, axis='index')

lower    a    b    c    d    e
range                         
0      150  150  150  150  150
1      142  142  142  142  142
2      134  134  134  134  134
3      126  126  126  126  126
4      118  118  118  118  118

原来axis='index'是的同义词axis=0
由于是axis='columns'同义axis=1

df0.add(s2, axis=0)

lower    a    b    c    d    e
range                         
0      150  150  150  150  150
1      142  142  142  142  142
2      134  134  134  134  134
3      126  126  126  126  126
4      118  118  118  118  118

其余操作

df0.sub(s2, axis=0)

lower   a   b   c   d   e
range                    
0      50  50  50  50  50
1      58  58  58  58  58
2      66  66  66  66  66
3      74  74  74  74  74
4      82  82  82  82  82

df0.mul(s2, axis=0)

lower     a     b     c     d     e
range                              
0      5000  5000  5000  5000  5000
1      4200  4200  4200  4200  4200
2      3400  3400  3400  3400  3400
3      2600  2600  2600  2600  2600
4      1800  1800  1800  1800  1800

df0.div(s2, axis=0)

lower         a         b         c         d         e
range                                                  
0      2.000000  2.000000  2.000000  2.000000  2.000000
1      2.380952  2.380952  2.380952  2.380952  2.380952
2      2.941176  2.941176  2.941176  2.941176  2.941176
3      3.846154  3.846154  3.846154  3.846154  3.846154
4      5.555556  5.555556  5.555556  5.555556  5.555556

df0.pow(1 / s2, axis=0)

lower         a         b         c         d         e
range                                                  
0      1.096478  1.096478  1.096478  1.096478  1.096478
1      1.115884  1.115884  1.115884  1.115884  1.115884
2      1.145048  1.145048  1.145048  1.145048  1.145048
3      1.193777  1.193777  1.193777  1.193777  1.193777
4      1.291550  1.291550  1.291550  1.291550  1.291550