【问题标题】:How do I operate on a DataFrame with a Series for every column?如何对每列都有一个系列的 DataFrame 进行操作?
【发布时间】:2018-11-08 23:17:10
【问题描述】:

问题

给定一个SeriessDataFramedf,如何对dfs的每一列进行操作?

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

目标和动机

我已经多次看到此类问题,并且看到了许多涉及其中某些元素的其他问题。最近,我不得不花一些时间在 cmets 中解释这个概念,同时寻找合适的规范问答。我没有找到,所以我想我会写一个。

这些问题通常与特定运算有关,但同样适用于大多数算术运算。

  • 如何从DataFrame 的每一列中减去Series
  • 如何在DataFrame 的每一列中添加Series
  • 如何将SeriesDataFrame 中的每一列相乘?
  • 如何将SeriesDataFrame 中的每一列分开?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    创建SeriesDataFrame 对象是什么的心智模型会很有帮助。

    剖析Series

    Series 应被视为增强型字典。这并不总是一个完美的类比,但我们将从这里开始。此外,您还可以进行其他类比,但我以字典为目标是为了展示这篇文章的目的。

    index

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

    values

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

    剖析DataFrame

    应将DataFrame 视为Series 的字典或SeriesSeries。在这种情况下,键是列名,值是作为Series 对象的列本身。每个Series 同意共享相同的index,这是DataFrame 的索引。

    columns

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

    index

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

    注意:RE:columnsindex 对象

    它们是同一种东西。 DataFrames index 可以用作另一个 DataFrames columns。事实上,当您使用df.T 获得转置时,就会发生这种情况。

    values

    这是一个二维数组,包含DataFrame 中的数据。现实情况是,values不是存储在 DataFrame 对象中的内容。 (嗯,有时是这样,但我不打算尝试描述块管理器)。关键是,最好将其视为对数据的二维数组的访问。


    定义样本数据

    这些是示例pandas.Index 对象,可用作SeriesDataFrameindex,或可用作DataFramecolumns

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

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

    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.Index 对象的示例pandas.DataFrame 对象:

    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
    )
    

    Series Series

    在两个Series上操作时,对齐是很明显的。您将一个Seriesindex 与另一个index 对齐。

    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
    

    它在两个轴上随机播放第二个 DataFrameindexcolumns 仍然会对齐并给我们同样的东西。

    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 被认为是SeriesSeries 的字典被认为是值的字典,那么在DataFrameSeries 之间操作时很自然它们应该通过它们的“键”对齐。

    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
    

    而当我们操作时,s0['a'] 中的10 会被添加到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 将 s2indexdf0columns 对齐。结果的columns 包括s2indexdf0columns 的并集。

    我们可以通过巧妙的换位来伪造它:

    (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
    

    但事实证明 Pandas 有更好的解决方案。有一些操作方法允许我们传递一个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
    

    首先解决一些更高层次的概念很重要。由于我的动机是分享知识和教学,因此我想尽可能清楚地说明这一点。

    【讨论】:

    • 对我来说标记 dup 以备将来问题的另一个好资源。 :-)
    • 另一种方法是通过广播df[df.columns] = df.values+s.values[:,None]
    【解决方案2】:

    我更喜欢mentioned by piSquared 方法(即df.add(s, axis=0)),但另一种方法使用applylambda 对数据框中的每一列执行操作:

    >>>> df.apply(lambda col: col + s)
        a   b   c
    0   4   5   6
    1  18  19  20
    

    要将 lambda 函数应用于行,请使用 axis=1

    >>> df.T.apply(lambda row: row + s, axis=1)
       0   1
    a  4  18
    b  5  19
    c  6  20
    

    当转换更复杂时,此方法可能很有用,例如:

    df.apply(lambda col: 0.5 * col ** 2 + 2 * s - 3)
    

    【讨论】:

    • 如果我没记错的话,基本上你可以简单地在第一个代码的末尾添加.T,而不是使用axis =1
    【解决方案3】:

    只是根据我自己的经验添加一个额外的层。它扩展了其他人在这里所做的事情。这显示了如何使用 DataFrame 对具有要保留其值的额外列的 Series 进行操作。下面是该过程的简短演示。

    import pandas as pd
    
    d = [1.056323, 0.126681, 
         0.142588, 0.254143,
         0.15561, 0.139571,
         0.102893, 0.052411]
         
    df = pd.Series(d, index = ['const', '426', '428', '424', '425', '423', '427', '636'])
    
    print(df)
    const    1.056323
    426      0.126681
    428      0.142588
    424      0.254143
    425      0.155610
    423      0.139571
    427      0.102893
    636      0.052411
    
    d2 = {
    'loc': ['D', 'D', 'E', 'E', 'F', 'F', 'G', 'G', 'E', 'D'],
    '426': [9, 2, 3, 2, 4, 0, 2, 7, 2, 8],
    '428': [2, 4, 1, 0, 2, 1, 3, 0, 7, 8],
    '424': [1, 10, 5, 8, 2, 7, 10, 0, 3, 5],
    '425': [9, 2, 6, 8, 9, 1, 7, 3, 8, 6],
    '423': [4, 2, 8, 7, 9, 6, 10, 5, 9, 9],
    '423': [2, 7, 3, 10, 8, 1, 2, 9, 3, 9],
    '427': [4, 10, 4, 0, 8, 3, 1, 5, 7, 7],
    '636': [10, 5, 6, 4, 0, 5, 1, 1, 4, 8],
    'seq': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    }
    
    df2 = pd.DataFrame(d2)
    
    print(df2)
      loc  426  428  424  425  423  427  636  seq
    0   D    9    2    1    9    2    4   10    1
    1   D    2    4   10    2    7   10    5    1
    2   E    3    1    5    6    3    4    6    1
    3   E    2    0    8    8   10    0    4    1
    4   F    4    2    2    9    8    8    0    1
    5   F    0    1    7    1    1    3    5    1
    6   G    2    3   10    7    2    1    1    1
    7   G    7    0    0    3    9    5    1    1
    8   E    2    7    3    8    3    7    4    1
    9   D    8    8    5    6    9    7    8    1
    

    DataFrame 乘以Series 并保留不同的列

    1. DataFrameSeries 中创建要操作的元素列表:
    col = ['426', '428', '424', '425', '423', '427', '636']
    
    1. 使用列表执行操作并指明要使用的轴:
    df2[col] = df2[col].mul(df[col], axis=1)
    
    print(df2)
      loc       426       428       424      425       423       427       636  seq
    0   D  1.140129  0.285176  0.254143  1.40049  0.279142  0.411572  0.524110    1
    1   D  0.253362  0.570352  2.541430  0.31122  0.976997  1.028930  0.262055    1
    2   E  0.380043  0.142588  1.270715  0.93366  0.418713  0.411572  0.314466    1
    3   E  0.253362  0.000000  2.033144  1.24488  1.395710  0.000000  0.209644    1
    4   F  0.506724  0.285176  0.508286  1.40049  1.116568  0.823144  0.000000    1
    5   F  0.000000  0.142588  1.779001  0.15561  0.139571  0.308679  0.262055    1
    6   G  0.253362  0.427764  2.541430  1.08927  0.279142  0.102893  0.052411    1
    7   G  0.886767  0.000000  0.000000  0.46683  1.256139  0.514465  0.052411    1
    8   E  0.253362  0.998116  0.762429  1.24488  0.418713  0.720251  0.209644    1
    9   D  1.013448  1.140704  1.270715  0.93366  1.256139  0.720251  0.419288    1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-12
      • 2014-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多