NumPy一维数组二维数组与Pandas的SeriesDataFrame行列结构和横纵方向的统一说明
Posted Laurence Geng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NumPy一维数组二维数组与Pandas的SeriesDataFrame行列结构和横纵方向的统一说明相关的知识,希望对你有一定的参考价值。
最近在这个问题上耽误了一些时间,原因是之前个人理解上出了一些偏差,又受到错误文章的误导,把这个问题搞复杂了,现在统一梳理一下。在展开之前,先明确说明的是:NumPy的二维数组与Pandas的DataFrame,它们的横纵方向和行列结构完全遵循常规,它们的字面量形式与常规二维表格数据(如CSV)的书写和阅读习惯一致,不存在任何“反常识”或“反直觉”的设计。本文地址:https://blog.csdn.net/bluishglc/article/details/128627618,转载请注明出处。
1.(在NumPy二维数组中的)一维数组是“行”还是“列”?
是“行”。首先,我们应该清楚一点:一维数组本身是没有“横向”和“纵向”概念的,站在一维数组里,我们只能分清是“向前”还是“向后”,即:在任何一个单一维度里,“方向”只有“正向”和“反向”之分。之所以会这样问是因为:一维数组是组成二维数组的基础元素,受二维数组对应二维表格的影响,人们需要确定一维数组对应的是表格中的“行”还是“列”。
和所有编程语言一样,在NumPy二维数组中的一维数组对应的是“行”,这个问题简单清楚,无需讨论。但是此前个人在理解这个问题时犯了一个低级错误:由于DataFrame是由Series构成的,既然使用NumPy二维数组可以构建DataFrame,于是就想当然地认为:NumPy二维数组中的一维数组和DataFrame里面的Series是一一对应的,由于Series是面向列设计的,所以就错误地把一维数组也理解为纵向的列了,进而认为NumPy的二维数组是由其内部一维数组以列的形式填充起来的。
所以,虽然很基础,但还是要再次强调一下:尽管我们可以简单地认为NumPy的二维数组对应Pandas的DataFrame,但是NumPy二维数组中的一维数组绝不对应DataFrame的Series,前者是一“行”数据,后者则是一“列”数据。
2.(在DataFrame中的)Series是“行”还是“列”?
是“列”。Series是DataFrame独有的数据结构,是构成DataFrame的下层数据结构,它是专门面向“列”设计的。我们先通过一张形象的Series结构示意图(图片引用自微信公众号:数据统计学)了解一下Series:
首先Series是有Index(行标签)的,其次,我们可以从DataFrame中取出一个Series打印一下内容就清楚了:
import numpy as np
import pandas as pd
a = np.array([[1, 1, 1],
[2, 2, 2]])
df = pd.DataFrame(a, columns=['col1', 'col2', 'col3'], index=['row1', 'row2'])
# 直接使用列名即可得到对应的Series
s = df.col1
print(f"type of s: type(s)")
print(f"data of s: s")
程序输出:
type of s: <class 'pandas.core.series.Series'>
data of s: row1 1
row2 2
Name: col1, dtype: int32
可见,在DataFrame,可以直接使用列名取出一个Series。
3. NumPy二维数组与DataFrame行列结构是否一致?
完全一致!它们的字面量形式与常规二维表格数据(如CSV)的行列布局一致:横向为行,纵向为列。符合常识,也遵循人们的使用习惯。以CSV数据为例:
name,gender,age
jack,male,23
rose,female,21
这是最常见的二维表格形态的数据,横向为行,纵向为列,人们非常习惯书写和阅读这种表达形式的二维数据。NumPy和Pandas不会设计与人们使用习惯向左的数据结构,使用NumPy二维数组表示上述CSV数据的表达式是:
import numpy as np
import pandas as pd
# 使用二维数组定义二维表格数据遵循人们的使用习惯:横向为行,纵向为列
roster = np.array([['jack', 'male', '23'],
['rose', 'female', '21']])
# 输出的行列数也是按人们的使用习惯定义的:(行,列)
print(f"shape of roster: roster.shape")
print(f"data of roster: \\nroster")
程序输出:
shape of roster: (2, 3)
data of roster:
[['jack' 'male' '23']
['rose' 'female' '21']]
按下来,我们再把这个NumPy的二维数组填充到一个DataFrame中,
roster = pd.DataFrame(roster, columns=['name', 'gender', 'age'])
print(f"shape of roster: roster.shape")
print(f"data of roster: \\nroster")
程序输出:
shape of roster: (2, 3)
data of roster:
name gender age
0 jack male 23
1 rose female 21
从几乎无差异的输出可以看出:DataFrame和NumPy对二维数据的“行”、“列”认定是一致的,也都遵从了人们的使用习惯。
4. 关于轴向(Axis)的再思考
在梳理完DataFrame和NumPy对二维数据的“行”、“列”认定方式后,我们再来思考一下轴向(Axis),我们知道Axis=0是纵向,Axis=1是横向,此前,个人曾写过一篇文章讨论如何理解和记忆它们,实际上,结合今天讨论的“行”、“列”问题,我突然意识到,或许很强有力的理由是:习惯!在一个二维表格里,聚合和过滤一般是面向什么操作的?列!求总和,求均值,这些操作都是在列上进行的,SQL中大量的函数都是面向列的聚合计算,既然列是主要的轴向操作,那么就应该设为默认值,从而不必每次显式设定,既然又是从0编码,不如就用初值0代表默认轴向(纵向),然后axis参数默认赋初值0,默认按列聚合或过滤,一切遵循默认的使用习惯**,这大概是轴向(Axis)如此定义的主要原因吧。
5. 两处容易出错的细节
最后,梳理一下近期在学习和使用DataFrame和NumPy过程中犯的两处与二维数据行列结构有关的错误:
(1) 当我们使用已有的一维数组去构建一个DataFrame时,要注意一下你手上的一维数组是行数据还是列数据,不要弄混,只有使用行数据构建才能得到符合预期的结果,如果是列数据,一定要使用DataFrame的列式构建形成创建DataFrame。此前在研究正态分布时,曾经分别生成过heights和weights两个数组,后来需要将它们合并一个二维数组显示在散点图中,横轴为height,纵轴为weight,于是就按下面的方式构建了DataFrame:
heights = np.random.normal(loc=170, scale=4, size=100)
weights = np.random.normal(loc=60, scale=4, size=100)
# 一个搞混了行列结构的错误示例
heights_weights = pd.DataFrame([heights,weights])
....
这里犯的错误就是把放有同一类数据本该当“列”的一维数组错当成了行,导致数组结构完全错位。修正方法就是使用DataFrame的列式构建形式创建它:pd.DataFrame('height':heights, 'weight': weights)
(2) 依然是前面提到过的问题,虽然Series是组成DataFrame的下层数据结构,但是:这并不代表使用NumPy二维数组填充DataFrame时里面的一维数组会被封装成Series,从而变成了DataFrame的列,这是完全错误的!下图就是一个错误案例(行列搞反了):
以上是关于NumPy一维数组二维数组与Pandas的SeriesDataFrame行列结构和横纵方向的统一说明的主要内容,如果未能解决你的问题,请参考以下文章