Pandas 什么时候默认广播系列和数据帧?
When does Pandas default to broadcasting Series and Dataframes?
我在尝试回答 时遇到了一些(对我来说)好奇的事情。
假设我想将一系列形状 (10,) 与形状 (10,10) 的 df 进行比较:
np.random.seed(0)
my_ser = pd.Series(np.random.randint(0, 100, size=10))
my_df = pd.DataFrame(np.random.randint(0, 100, size=100).reshape(10,10))
my_ser > 10 * my_df
如预期的那样产生 df (10,10) 形状的矩阵。比较似乎是按行进行的。
但是考虑这种情况:
df = pd.DataFrame({'cell1':[0.006209, 0.344955, 0.004521, 0, 0.018931, 0.439725, 0.013195, 0.009045, 0, 0.02614, 0],
'cell2':[0.048043, 0.001077, 0,0.010393, 0.031546, 0.287264, 0.016732, 0.030291, 0.016236, 0.310639,0],
'cell3':[0,0,0.020238, 0, 0.03811, 0.579348, 0.005906, 0,0,0.068352, 0.030165],
'cell4':[0.016139, 0.009359, 0,0,0.025449, 0.47779, 0, 0.01282, 0.005107, 0.004846, 0],
'cell5': [0,0,0,0.012075, 0.031668, 0.520258, 0,0,0,2.728218, 0.013418]})
i = 0
df.iloc[:,i].shape
>(11,)
(10 * df.drop(df.columns[i], axis=1)).shape
>(11,4)
(df.iloc[:,i] > (10 * df.drop(df.columns[i], axis=1))).shape
>(11,15)
据我所知,这里 Pandas 使用 df 广播系列。这是为什么?
可以通过以下方式获得所需的行为:
(10 * df.drop(df.columns[i], axis=1)).lt(df.iloc[:,i], axis=0).shape
>(11,4)
pd.__version__
'0.24.0'
底线、Pandas 将每个系列值与标题与值索引匹配的列进行比较。第二个示例中的索引是 0..10,列名 cell1..4
,因此没有匹配的列名,您只需附加新列。这实质上是将系列视为一个数据框,索引作为列标题。
如果您使系列的长度超过列数,您实际上可以看到 pandas 在第一个示例中所做的部分操作:
>>> my_ser = pd.Series(np.random.randint(0, 100, size=20))
>>> my_df
0 1 2 3 4
0 9 10 27 45 71
1 39 61 85 97 44
2 34 34 88 33 5
3 36 0 75 34 69
4 53 80 62 8 61
5 1 81 35 91 40
6 36 48 25 67 35
7 30 29 33 18 17
8 93 84 2 69 12
9 44 66 91 85 39
>>> my_ser
0 92
1 36
2 25
3 32
4 42
5 14
6 86
7 28
8 20
9 82
10 68
11 22
12 99
13 83
14 7
15 72
16 61
17 13
18 5
19 0
dtype: int64
>>> my_ser>my_df
0 1 2 3 4 5 6 7 8 9 \
0 True True False False False False False False False False
1 True False False False False False False False False False
2 True True False False True False False False False False
3 True True False False False False False False False False
4 True False False True False False False False False False
5 True False False False True False False False False False
6 True False False False True False False False False False
7 True True False True True False False False False False
8 False False True False True False False False False False
9 True False False False True False False False False False
10 11 12 13 14 15 16 17 18 19
0 False False False False False False False False False False
1 False False False False False False False False False False
2 False False False False False False False False False False
3 False False False False False False False False False False
4 False False False False False False False False False False
5 False False False False False False False False False False
6 False False False False False False False False False False
7 False False False False False False False False False False
8 False False False False False False False False False False
9 False False False False False False False False False False
注意发生了什么 - 92 与第一列进行比较,因此您在 93 处得到一个 False
。然后 36 与第二列进行比较,依此类推。如果您的系列的长度与您的数量相匹配列,然后您将获得预期的行为。
但是当你的系列更长时会发生什么?那么,您需要将一个新的假列附加到数据框以继续进行比较。里面装的是什么?我没有找到任何文档,但我的印象是它只是填写了 False,因为没有什么可比较的。因此你会得到额外的列来匹配系列长度,所有 False
.
但是你的例子呢?你没有得到11列,而是4+11=15!我们再做一个测试:
>>> my_df = pd.DataFrame(np.random.randint(0, 100, size=100).reshape(10,10),columns=[chr(i) for i in range(10)])
>>> my_ser = pd.Series(np.random.randint(0, 100, size=10))
>>> (my_df>my_ser).shape
(10, 20)
这次我们得到维度之和,10+10=20,作为输出列的数量!
有什么区别? Pandas 将每个系列索引与匹配的列标题进行比较。在您的第一个示例中, my_ser
和 my_df
标题的索引匹配,因此它比较了它们。如果有额外的列 - 以上就是发生的情况。如果所有列的名称与系列索引不同,那么所有列都是额外的,你会得到你的结果,在我的例子中会发生什么,标题现在是字符,索引整数。
正在发生的事情是 pandas 使用内部数据对齐。 Pandas 几乎总是对齐索引上的数据,行索引或列索引 headers。这是一个简单的例子:
s1 = pd.Series([1,2,3], index=['a','b','c'])
s2 = pd.Series([2,4,6], index=['a','b','c'])
s1 + s2
#Ouput as expected:
a 3
b 6
c 9
dtype: int64
现在,让我们 运行 其他几个具有不同索引的示例:
s2 = pd.Series([2,4,6], index=['a','a','c'])
s1 + s2
#Ouput
a 3.0
a 5.0
b NaN
c 9.0
dtype: float64
笛卡尔积出现重复索引,匹配为NaN + value = NaN。
并且,没有匹配的索引:
s2 = pd.Series([2,4,6], index=['e','f','g'])
s1 + s2
#Output
a NaN
b NaN
c NaN
e NaN
f NaN
g NaN
dtype: float64
因此,在您的第一个示例中,您创建的 pd.Series 和 pd.DataFrame 具有匹配的默认范围索引,因此比较按预期进行。在您的第二个示例中,您将列 headers ['cell2','cell3','cell4','cell5'] 与默认范围索引进行比较,该索引返回所有 15列且不匹配所有值将为 False,NaN 比较 returns False.
我在尝试回答
假设我想将一系列形状 (10,) 与形状 (10,10) 的 df 进行比较:
np.random.seed(0)
my_ser = pd.Series(np.random.randint(0, 100, size=10))
my_df = pd.DataFrame(np.random.randint(0, 100, size=100).reshape(10,10))
my_ser > 10 * my_df
如预期的那样产生 df (10,10) 形状的矩阵。比较似乎是按行进行的。
但是考虑这种情况:
df = pd.DataFrame({'cell1':[0.006209, 0.344955, 0.004521, 0, 0.018931, 0.439725, 0.013195, 0.009045, 0, 0.02614, 0],
'cell2':[0.048043, 0.001077, 0,0.010393, 0.031546, 0.287264, 0.016732, 0.030291, 0.016236, 0.310639,0],
'cell3':[0,0,0.020238, 0, 0.03811, 0.579348, 0.005906, 0,0,0.068352, 0.030165],
'cell4':[0.016139, 0.009359, 0,0,0.025449, 0.47779, 0, 0.01282, 0.005107, 0.004846, 0],
'cell5': [0,0,0,0.012075, 0.031668, 0.520258, 0,0,0,2.728218, 0.013418]})
i = 0
df.iloc[:,i].shape
>(11,)
(10 * df.drop(df.columns[i], axis=1)).shape
>(11,4)
(df.iloc[:,i] > (10 * df.drop(df.columns[i], axis=1))).shape
>(11,15)
据我所知,这里 Pandas 使用 df 广播系列。这是为什么?
可以通过以下方式获得所需的行为:
(10 * df.drop(df.columns[i], axis=1)).lt(df.iloc[:,i], axis=0).shape
>(11,4)
pd.__version__
'0.24.0'
底线、Pandas 将每个系列值与标题与值索引匹配的列进行比较。第二个示例中的索引是 0..10,列名 cell1..4
,因此没有匹配的列名,您只需附加新列。这实质上是将系列视为一个数据框,索引作为列标题。
如果您使系列的长度超过列数,您实际上可以看到 pandas 在第一个示例中所做的部分操作:
>>> my_ser = pd.Series(np.random.randint(0, 100, size=20))
>>> my_df
0 1 2 3 4
0 9 10 27 45 71
1 39 61 85 97 44
2 34 34 88 33 5
3 36 0 75 34 69
4 53 80 62 8 61
5 1 81 35 91 40
6 36 48 25 67 35
7 30 29 33 18 17
8 93 84 2 69 12
9 44 66 91 85 39
>>> my_ser
0 92
1 36
2 25
3 32
4 42
5 14
6 86
7 28
8 20
9 82
10 68
11 22
12 99
13 83
14 7
15 72
16 61
17 13
18 5
19 0
dtype: int64
>>> my_ser>my_df
0 1 2 3 4 5 6 7 8 9 \
0 True True False False False False False False False False
1 True False False False False False False False False False
2 True True False False True False False False False False
3 True True False False False False False False False False
4 True False False True False False False False False False
5 True False False False True False False False False False
6 True False False False True False False False False False
7 True True False True True False False False False False
8 False False True False True False False False False False
9 True False False False True False False False False False
10 11 12 13 14 15 16 17 18 19
0 False False False False False False False False False False
1 False False False False False False False False False False
2 False False False False False False False False False False
3 False False False False False False False False False False
4 False False False False False False False False False False
5 False False False False False False False False False False
6 False False False False False False False False False False
7 False False False False False False False False False False
8 False False False False False False False False False False
9 False False False False False False False False False False
注意发生了什么 - 92 与第一列进行比较,因此您在 93 处得到一个 False
。然后 36 与第二列进行比较,依此类推。如果您的系列的长度与您的数量相匹配列,然后您将获得预期的行为。
但是当你的系列更长时会发生什么?那么,您需要将一个新的假列附加到数据框以继续进行比较。里面装的是什么?我没有找到任何文档,但我的印象是它只是填写了 False,因为没有什么可比较的。因此你会得到额外的列来匹配系列长度,所有 False
.
但是你的例子呢?你没有得到11列,而是4+11=15!我们再做一个测试:
>>> my_df = pd.DataFrame(np.random.randint(0, 100, size=100).reshape(10,10),columns=[chr(i) for i in range(10)])
>>> my_ser = pd.Series(np.random.randint(0, 100, size=10))
>>> (my_df>my_ser).shape
(10, 20)
这次我们得到维度之和,10+10=20,作为输出列的数量!
有什么区别? Pandas 将每个系列索引与匹配的列标题进行比较。在您的第一个示例中, my_ser
和 my_df
标题的索引匹配,因此它比较了它们。如果有额外的列 - 以上就是发生的情况。如果所有列的名称与系列索引不同,那么所有列都是额外的,你会得到你的结果,在我的例子中会发生什么,标题现在是字符,索引整数。
正在发生的事情是 pandas 使用内部数据对齐。 Pandas 几乎总是对齐索引上的数据,行索引或列索引 headers。这是一个简单的例子:
s1 = pd.Series([1,2,3], index=['a','b','c'])
s2 = pd.Series([2,4,6], index=['a','b','c'])
s1 + s2
#Ouput as expected:
a 3
b 6
c 9
dtype: int64
现在,让我们 运行 其他几个具有不同索引的示例:
s2 = pd.Series([2,4,6], index=['a','a','c'])
s1 + s2
#Ouput
a 3.0
a 5.0
b NaN
c 9.0
dtype: float64
笛卡尔积出现重复索引,匹配为NaN + value = NaN。
并且,没有匹配的索引:
s2 = pd.Series([2,4,6], index=['e','f','g'])
s1 + s2
#Output
a NaN
b NaN
c NaN
e NaN
f NaN
g NaN
dtype: float64
因此,在您的第一个示例中,您创建的 pd.Series 和 pd.DataFrame 具有匹配的默认范围索引,因此比较按预期进行。在您的第二个示例中,您将列 headers ['cell2','cell3','cell4','cell5'] 与默认范围索引进行比较,该索引返回所有 15列且不匹配所有值将为 False,NaN 比较 returns False.