数据科学 IPython 笔记本 9.3 理解 Python 中的数据类型

9.3 理解 Python 中的数据类型

本节是《Python 数据科学手册》(Python Data Science Handbook)的摘录。

译者:飞龙

协议:CC BY-NC-SA 4.0

数据驱动的科学和有效计算需要了解数据的存储和操作方式。本节概述了如何在 Python 语言本身中处理数据数组,以及对比 NumPy 如何改进它。对于理解本书其余部分的大部分内容,理解这种差异至关重要。

Python 的用户通常被它的易用性吸引,其中一部分是动态类型。虽然像 C 或 Java 这样的静态类型语言要求显式声明每个变量,但像 Python 这样的动态类型语言会跳过此规范。 例如,在 C 中,你可以指定特定操作,如下所示:

/* C 代码 */
int result = 0;
for(int i=0; i<100; i++){
    result += i;
}

在 Python 中,可以用这种方式编写等效的操作:

# Python 代码
result = 0
for i in range(100):
    result += i

注意主要区别:在 C 中,每个变量的数据类型是显式声明的,而在 Python 中,类型是动态推断的。 这意味着,例如,我们可以将任何类型的数据分配给任何变量:

# Python 代码
x = 4
x = "four"

这里我们将x的内容从整数转换为字符串。 C 中的相同内容会导致编译错误或其他无意义的结果(取决于编译器设置):

/* C 代码 */
int x = 4;
x = "four";  // 失败

这种灵活性,是使 Python 和其他动态类型语言方便易用的一个方面。理解它的原理,是学习如何有效使用 Python 分析数据的一个重要方面。

但是这种类型的灵活性也指出了,Python 变量不仅仅是它们的值; 它们还包含值的类型的额外信息。 我们将在后面的章节中详细探讨它。

Python 的整数不仅仅是整数

标准的 Python 实现是用 C 编写的。这意味着每个 Python 对象都只是一个巧妙伪装的 C 结构,它不仅包含其值,还包含其他信息。

例如,当我们在 Python 中定义一个整数时,例如x = 10000x不仅仅是一个“原始”整数。 它实际上是指向复合 C 结构的指针,包含多个值。

通过 Python 3.4 源代码,我们发现(长)整数类型定义实际上看起来像这样(C 宏扩展之后):

struct _longobject {
    long ob_refcnt;
    PyTypeObject *ob_type;
    size_t ob_size;
    long ob_digit[1];
};

Python 3.4 中的单个整数实际上包含四个部分:

  • ob_refcnt, 引用计数,帮助 Python 静默处理内存分配和释放
  • ob_type, 它编码变量的类型
  • ob_size, 它指定以下数据成员的大小
  • ob_digit, 其中包含我们期望 Python 变量表示的实际整数值。

这意味着在 Python 中存储整数,与在 C 等编译语言中的整数相比,存在一些开销,如下图所示:

Integer Memory Layout

这里PyObject_HEAD是结构的一部分,包含引用计数,类型代码和之前提到的其他部分。

注意这里的区别:C 整数本质上是内存中位置的标签,它的字节编码整数值。Python 整数是指针,指向内存中包含所有 Python 对象信息的位置,包含编码整数值的字节。Python 整数结构中的这些额外信息,允许 Python 自由动态地编码。

然而,Python 类型中的所有这些附加信息都需要付出代价,这在组合了许多这些对象的结构中尤为明显。

Python 列表不仅仅是列表

现在让我们考虑,当我们使用包含许多 Python 对象的 Python 数据结构时会发生什么。

Python 中的标准可变多元素容器就是列表。我们可以创建一个整数列表,如下所示:

L = list(range(10))
L

# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

type(L[0])

# int

或者,类似地,字符串列表:

L2 = [str(c) for c in L]
L2

# ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

type(L2[0])

# str

由于 Python 的动态类型,我们甚至可以创建异构列表:

L3 = [True, "2", 3.0, 4]
[type(item) for item in L3]

# [bool, str, float, int]

但是这种灵活性需要付出代价:为了允许这些灵活类型,列表中的每个项目都必须包含自己的类型信息,引用计数和其他信息 - 也就是说,每个项目都是完整的 Python 对象。在所有变量属于同一类型的特殊情况下,大部分信息都是冗余的:将数据存储在固定类型数组中会更加高效。

动态类型列表和固定类型(NumPy 样式)数组之间的区别如下图所示:

Array Memory Layout

在实现级别,数组基本上包含指向一个连续数据块的单个指针。

另一方面,Python 列表包含一个指向指针块的指针,每个指针指向一个完整的 Python 对象,就像我们之前看到的 Python 整数一样。

同样,列表的优点是灵活性:因为每个列表元素是包含数据和类型信息的完整结构,所以列表可以填充为任何所需类型的数据。固定类型的 NumPy 风格数组缺乏这种灵活性,但是对于存储和操作数据更有效。

Python 中固定类型的数组

Python提供了几种不同的选项,用于在固定类型数据缓冲区中高效存储数据。内置的array模块(自 Python 3.3 起可用)可用于创建统一类型的密集数组:

import array
L = list(range(10))
A = array.array('i', L)
A

# array('i', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

这里'i'是一个类型代码,表示内容是整数。然而,更有用的是 NumPy 包的ndarray对象。

虽然Python的array对象提供了基于数组的,数据的有效存储,但 NumPy 在数组上添加了高效操作。我们将在后面的章节中探讨这些操作; 在这里,我们将演示创建 NumPy 数组的几种方法。

我们将从别名为np的标准 NumPy 导入开始:

import numpy as np

从 Python 列表创建数组

首先,我们可以使用np.array从 Python 列表创建数组:

# 整数数组
np.array([1, 4, 2, 5, 3])

# array([1, 4, 2, 5, 3])

请记住,与 Python 列表不同,NumPy 仅限于类型相同的数组。
如果类型不匹配,NumPy 将尽可能向上转换(此处,整数向上转换为浮点数):

np.array([3.14, 4, 2, 3])

# array([ 3.14,  4.  ,  2.  ,  3.  ])

如果我们想显式设置所得数组的数据类型,我们可以使用dtype关键字:

np.array([1, 2, 3, 4], dtype='float32')

# array([ 1.,  2.,  3.,  4.], dtype=float32)

最后,与 Python 列表不同,NumPy 数组可以是显式多维的; 这是一种方法,使用列表的列表初始化多维数组:

# 嵌套列表产生多维数组
np.array([range(i, i + 3) for i in [2, 4, 6]])

'''
array([[2, 3, 4],
       [4, 5, 6],
       [6, 7, 8]])
'''

内部列表被视为生成的二维数组的行。

从零开始创建数组

特别是对于较大的数组,使用 NumPy 中内置的例程从头开始创建数组效率更高。以下是几个例子:

# 创建长度为 10 的零填充的整数数组
np.zeros(10, dtype=int)

# array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])


# 创建一填充的 3x5 浮点数数组
np.ones((3, 5), dtype=float)

'''
array([[ 1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.]])
'''

# 创建 3.14 填充的 3x5 浮点数数组
np.full((3, 5), 3.14)

'''
array([[ 3.14,  3.14,  3.14,  3.14,  3.14],
       [ 3.14,  3.14,  3.14,  3.14,  3.14],
       [ 3.14,  3.14,  3.14,  3.14,  3.14]])
'''

# 创建数组,填充为 0 到 20 步长为 2 的线性序列
# (类似于内建的 range() 函数)
np.arange(0, 20, 2)

# array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])

# 创建五个值的数组,从 0 到 1 等间隔
np.linspace(0, 1, 5)

# array([ 0.  ,  0.25,  0.5 ,  0.75,  1.  ])

# 创建 3x3 数组,包含 0 到 1 均匀分布随机值
np.random.random((3, 3))

'''
array([[ 0.99844933,  0.52183819,  0.22421193],
       [ 0.08007488,  0.45429293,  0.20941444],
       [ 0.14360941,  0.96910973,  0.946117  ]])
'''

# 创建 3x3 数组,包含均值为 0 标准差为 1 的正态分布随机值
np.random.normal(0, 1, (3, 3))

'''
array([[ 1.51772646,  0.39614948, -0.10634696],
       [ 0.25671348,  0.00732722,  0.37783601],
       [ 0.68446945,  0.15926039, -0.70744073]])
'''

# 创建 3x3 数组,包含 [0, 10) 中的随机值
np.random.randint(0, 10, (3, 3))

'''
array([[2, 3, 4],
       [5, 7, 8],
       [0, 5, 0]])
'''

# 创建 3x3 单位矩阵
np.eye(3)

'''
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
'''

# 创建三个整数的未初始化数组
# 值是内存地址中已经存在的任何东西
np.empty(3)

# array([ 1.,  1.,  1.])

NumPy 标准数据类型

NumPy 数组包含类型单一的值,因此详细了解这些类型及其限制非常重要。由于 NumPy 是用 C 语言构建的,因此 C,Fortran 和其他相关语言的用户会熟悉这些类型。标准 NumPy 数据类型列在下表中。

请注意,在构造数组时,可以使用字符串指定它们:

np.zeros(10, dtype='int16')

或者使用相关的 NumPy 对象:

np.zeros(10, dtype=np.int16)
数据类型 描述
bool_ 布尔值(True 或 False)储存为字节
int_ 默认整数类型(与 C long相同;通常是int64int32
intc 等价于 C int(normally int32 or int64
intp 用于索引的整数(与 C ssize_t相同;通常是int32int64
int8 字节(-128 到 127)
int16 整数(-32768 到 32767)
int32 整数(-2147483648 到 2147483647)
int64 整数(-9223372036854775808 到 9223372036854775807)
uint8 无符号整数(0 到 255)
uint16 无符号整数(0 到 65535)
uint32 无符号整数(0 到 4294967295)
uint64 无符号整数(0 到 18446744073709551615)
float_ float64的简写
float16 半精度浮点: 符号位,5 位指数,10 位尾数
float32 单精度浮点: 符号位,8 位指数,23 位尾数
float64 双精度浮点: 符号位,11 位指数,52 位尾数
complex_ complex128的简写
complex64 复数,表示为两个 32 位浮点
complex128 复数,表示为两个 64 位浮点

更高级的类型规范是可能的,例如指定大或小端编码;更多信息请参阅 NumPy 文档

NumPy 还支持复合数据类型,这将在结构化数据:NumPy 的结构化数组中介绍。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 基础篇NumPy的主要对象是同种元素的多维数组。这是一个所有的元素都是一种类型、通过一个正整数元组索引的元素表格(...
    oyan99阅读 5,102评论 0 18
  • 我们之前一定听有人说过,python的执行速度比其他语言慢。 我们通常的解释是:python是一个动态的解释型语言...
    又迷鹿了阅读 9,035评论 1 7
  • 原文链接 《Python数据分析》(Python for Data Analysis, 2nd Edition)第...
    李绍俊阅读 8,210评论 0 5
  • Python语言特性 1 Python的函数参数传递 看两个如下例子,分析运行结果: 代码一: a = 1 def...
    伊森H阅读 3,042评论 0 15
  • (1) 源于对绘本的钟爱和在家圈不住的性格,前一段雾霾指数爆表的一天,我和恬恬毅然决然的来到了向往的绘本馆体验。 ...
    爽妈阅读 472评论 0 0