创建Series 和DataFrame 对象是什么的心智模型会很有帮助。
剖析Series
Series 应被视为增强型字典。这并不总是一个完美的类比,但我们将从这里开始。此外,您还可以进行其他类比,但我以字典为目标是为了展示这篇文章的目的。
index
这些是我们可以引用以获取相应值的键。当索引的元素是唯一的时,与字典的比较变得非常接近。
values
这些是由索引键入的相应值。
剖析DataFrame
应将DataFrame 视为Series 的字典或Series 的Series。在这种情况下,键是列名,值是作为Series 对象的列本身。每个Series 同意共享相同的index,这是DataFrame 的索引。
columns
这些是我们可以参考以获取相应Series 的键。
index
这是所有Series 值同意共享的索引。
注意:RE:columns 和 index 对象
它们是同一种东西。 DataFrames index 可以用作另一个 DataFrames columns。事实上,当您使用df.T 获得转置时,就会发生这种情况。
values
这是一个二维数组,包含DataFrame 中的数据。现实情况是,values不是存储在 DataFrame 对象中的内容。 (嗯,有时是这样,但我不打算尝试描述块管理器)。关键是,最好将其视为对数据的二维数组的访问。
定义样本数据
这些是示例pandas.Index 对象,可用作Series 或DataFrame 的index,或可用作DataFrame 的columns:
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上操作时,对齐是很明显的。您将一个Series 的index 与另一个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
它在两个轴上随机播放第二个 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
DataFrameSeries
如果DataFrames 被认为是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
而当我们操作时,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
问题的核心和帖子的重点
如果我想要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
这不会产生我们想要的结果,因为 Pandas 将 s2 的 index 与 df0 的 columns 对齐。结果的columns 包括s2 的index 和df0 的columns 的并集。
我们可以通过巧妙的换位来伪造它:
(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
首先解决一些更高层次的概念很重要。由于我的动机是分享知识和教学,因此我想尽可能清楚地说明这一点。