Как мне работать с DataFrame с серией для каждого столбца - PullRequest
0 голосов
/ 09 ноября 2018

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

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

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

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

Вопрос

С учетом Series s и DataFrame df. Как мне работать с каждым столбцом 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

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Я предпочитаю метод, упомянутый @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)
0 голосов
/ 09 ноября 2018

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


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

Анатомия Series

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

index

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

values

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

Анатомия DataFrame

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

columns

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

index

Это индекс, по которому все значения Series согласны разделить.

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

Это такие же вещи. A 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 с
Выравнивание очевидно и делает то, что мы должны делать

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 s следует рассматривать как словари 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...