NumPy一维数组、二维数组与Pandas的Series、DataFrame行列结构和横纵方向的统一说明

最近在这个问题上耽误了一些时间,原因是之前个人理解上出了一些偏差,又受到错误文章的误导,把这个问题搞复杂了,现在统一梳理一下。在展开之前,先明确说明的是: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: \n{roster}")

程序输出:

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: \n{roster}")

程序输出:

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的列,这是完全错误的!下图就是一个错误案例(行列搞反了):

在这里插入图片描述

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laurence 

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值