创建一个关于
Series
和
DataFrame
对象的心智模型会很有帮助。
Series
的解剖
Series
可以被视为增强版的字典。这并不总是一个完美的类比,但我们从这里开始。此外,您可以做出其他类比,但我选择字典来展示本文的目的。
index
这些是我们可以引用以获取相应值的键。当索引元素唯一时,与字典的比较非常接近。
values
这些是由索引键控的相应值。
DataFrame
的解剖
DataFrame
可以被视为
Series
字典或
Series
的字典。在这种情况下,键是列名,而值是作为
Series
对象的列本身。每个
Series
同意共享相同的
index
,这是
DataFrame
的索引。
columns
这些是我们可以引用以获取相应
Series
的键。
index
这是所有
Series
值都同意共享的索引。
注意:关于 columns
和 index
对象
它们是相同类型的对象。一个
DataFrame
的
index
可以用作另一个
DataFrame
的
columns
。事实上,当您执行
df.T
以获取转置时,就会发生这种情况。
values
这是包含
DataFrame
中数据的二维数组。现实情况是,
values
不是存储在
DataFrame
对象内部的内容。 (好吧,有时候是的,但我不打算尝试描述块管理器)。重点是,最好将其视为访问数据的二维数组。
定义示例数据
以下是可用作 Series
或 DataFrame
的 index
,或者可用作 DataFrame
的 columns
的示例 pandas.Index
对象:
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
on 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
Series
上的DataFrame
如果将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
当我们操作时,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
首先,需要解释一些较高层次的概念。由于我的动机是分享知识和教授技能,因此我希望尽可能清晰易懂。