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