Как мне работать с DataFrame с серией для каждого столбца

Цель и мотивация

Я видел такой вопрос несколько раз и видел много других вопросов, которые включают в себя некоторый элемент этого. Совсем недавно мне пришлось потратить немного времени на объяснение этой концепции в комментариях в поисках подходящих канонических вопросов и ответов. Я не нашел один, и поэтому я думал, что напишу один.

Этот вопрос обычно возникает в отношении конкретной операции, но в равной степени относится к большинству арифметических операций.

  • Как вычесть Series из каждого столбца в DataFrame?
  • Как мне добавить Series из каждого столбца в DataFrame?
  • Как мне умножить Series из каждого столбца в DataFrame?
  • Как мне разделить Series из каждого столбца в DataFrame?

Вопрос

Учитывая Seriess а также DataFramedf, Как мне оперировать на каждом столбце df с 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

3 ответа

Пожалуйста, несите преамбулу. Важно сначала обратиться к некоторым концепциям более высокого уровня. Поскольку моя мотивация - делиться знаниями и учить, я хотел сделать это как можно более ясным.


Полезно создать мысленную модель того, что Series а также DataFrame объекты есть.

Анатомия Series

Series следует рассматривать как расширенный словарь. Это не всегда идеальная аналогия, но мы начнем здесь. Также есть и другие аналогии, которые вы можете сделать, но я нацеливаюсь на словарь, чтобы продемонстрировать цель этого поста.

index

Это ключи, на которые мы можем ссылаться, чтобы получить соответствующие значения. Когда элементы индекса уникальны, сравнение со словарем становится очень близким.

values

Это соответствующие значения, которые указываются индексом.

Анатомия DataFrame

DataFrame следует рассматривать как словарь Series или Series из Series, В этом случае ключами являются имена столбцов, а значениями являются сами столбцы как Series объекты. каждый Series согласен поделиться тем же index который является индексом DataFrame,

columns

Это ключи, на которые мы можем ссылаться, чтобы получить на соответствующем Series,

index

Это индекс, который все Series ценности согласны делиться

Примечание: RE: columns а также index объекты

Это такие же вещи. DataFrame s index может быть использован как другой DataFrame s columns, На самом деле это происходит, когда вы делаете df.T чтобы получить транспонирование.

values

Это двумерный массив, который содержит данные в DataFrame, Реальность такова, что values НЕ то, что хранится внутри DataFrame объект. (Ну, иногда это так, но я не собираюсь пытаться описать менеджер блоков). Дело в том, что лучше думать об этом как о доступе к двумерному массиву данных.


Определить пример данных

Это образец pandas.Index объекты, которые могут быть использованы в качестве index из Series или же DataFrame или может быть использован в качестве 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
)

Series на Series

При работе на двух Series Выравнивание очевидно. Вы выравниваете index одного Series с 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, В этом случае у Панд нет 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

DataFrame на DataFrame

Подобное верно при работе между двумя DataFrame s
Выравнивание очевидно и делает то, что мы должны сделать

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 по обеим осям. index а также columns все равно выровняется и даст нам то же самое.

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

DataFrame на Series

Если DataFrame должны быть словарями Series а также Series следует понимать как словари ценностей, тогда естественно, что при работе между DataFrame а также 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

И когда мы работаем, 10 в s0['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

Суть вопроса и смысл поста

А если я захочу s2 а также df0?

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

Это не производит то, что мы хотели. Потому что Панды выравнивает index из s2 с columns из df0, columns результат включает в себя объединение index из s2 и columns из df0,

Мы могли бы подделать это хитрым транспонированием

(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

Я предпочитаю метод, упомянутый @piSquared (т.е. df.add(s, axis=0)), но другой метод использует apply вместе с lambda выполнить действие для каждого столбца в кадре данных:

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

Чтобы применить лямбда-функцию к строкам, используйте 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)

Просто чтобы добавить дополнительный слой из моего собственного опыта. Это расширяет то, что здесь сделали другие. Это показывает, как работать с a, у которого есть дополнительные столбцы, для которых вы хотите сохранить значения. Ниже представлена ​​краткая демонстрация процесса.

      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

Чтобы умножить a на a и сохранить разные столбцы

  1. Создайте список элементов в DataFrame а также Series вы хотите оперировать:
      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
Другие вопросы по тегам