Ответ 1
Пожалуйста, несите преамбулу. Важно сначала обратиться к некоторым концепциям более высокого уровня. Поскольку моя мотивация - делиться знаниями и учить, я хотел сделать это как можно более ясным.
Полезно создать мысленную модель объектов Series
и DataFrame
.
Анатомия Series
Series
должна рассматриваться как расширенный словарь. Это не всегда идеальная аналогия, но мы начнем здесь. Также есть и другие аналогии, которые вы можете сделать, но я нацеливаюсь на словарь, чтобы продемонстрировать цель этого поста.
index
Это ключи, на которые мы можем ссылаться, чтобы получить соответствующие значения. Когда элементы индекса уникальны, сравнение со словарем становится очень близким.
values
Это соответствующие значения, которые указываются индексом.
Анатомия DataFrame
DataFrame
следует рассматривать как словарь Series
или Series
Series
. В этом случае ключи - это имена столбцов, а значения - сами столбцы как объекты Series
. Каждая Series
соглашается использовать один и тот же index
который является индексом DataFrame
.
columns
Это ключи, на которые мы можем ссылаться, чтобы получить в соответствующей Series
.
index
Это индекс, который все значения Series
согласны разделить.
Примечание: RE: columns
и index
объекты
Это такие же вещи. DataFrame
index
может быть использован в качестве еще одного DataFrame
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
. В этом случае у 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
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
on 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