目录
前言
《利用Python进行数据分析》这本书是由Wes McKinney编写的,他是Python pandas项目的创始人。这本书详细介绍了如何使用pandas进行数据分析,以及如何利用Python的其他库,如NumPy和IPython,来处理、清洗和规整数据。
一、准备工作
1、使用Python进行数据分析的优势
本书第一章中对使用Python进行数据分析的优势进行了详细的讨论:
应用背景:
- Python的历史和社区:Python自1991年诞生以来,已经发展成为一个庞大而活跃的科学计算和数据分析社区的一部分。
- Python作为胶水语言:Python能够轻松集成C、C++以及Fortran代码,这使得它在科学计算中非常有用。
Python的优势:
- 易于学习和使用:Python语法简洁明了,使得新用户可以快速上手并进行有效编程;
- 强大的库生态系统:Python有着丰富的库,如NumPy和pandas,这些库提供了强大的数据处理和分析能力;
- 适合快速原型开发和迭代:Python适合快速开发原型和进行迭代,这对于数据分析项目的快速进展至关重要。
Python在数据分析中的具体应用:
- 结构化数据处理:Python特别适合处理结构化数据,这是数据分析中最常见的数据类型;
- 数据清洗和准备:Python的数据清洗能力非常强大,特别是使用pandas库时;
- 数据可视化:Python支持多种数据可视化工具,如matplotlib和seaborn,这对于分析结果的展示非常有帮助。
2、结构化数据
我们在进行数据分析时,用到的最主要的是结构化数据。结构化数据通常是如下数据:
- 表格型或电子表格型数据,其中各列可能是不同的类型(字符串、数值、日期等)。比如保存在关系型数据库中或以制表符/逗号为分隔符的文本文件中的那些数据。
- 多维数组(矩阵)。
- 通过关键列(对于SQL用户而言,就是主键和外键)相互联系的多张表。
- 平均或不平均间隔的时间序列。
大部分数据集都能被转化为更加适合分析和建模的结构化形式,也可以将数据集的特征提取为某种结构化形式。例如,一组新闻文章可以被处理为一张词频表,而这张词频表就可以用于情感分析。
3、Python的劣势
- 性能问题:作为一种解释型语言,Python在执行速度上通常不如编译型语言如C++或Java;
- 并发处理:Python的全局解释器锁(GIL)限制了它在多线程并发执行方面的能力。
4、Python数据分析中的库
NumPy
NumPy,是Numerical Python(数值Python)的简称,它提供了多种数据结构、算法以及大部分涉及Python数值计算所需的接口。NumPy提供了以下功能:
- 快速、高效的多维数组对象ndarray。
- 用于对数组执行元素级计算以及直接对数组执行数学运算的函数。
- 用于读写硬盘上基于数组的数据集的工具。
- 线性代数运算、傅里叶变换,以及随机数生成。
- 成熟的C API, 用于Python插件和原生C、C++、Fortran代码存取NumPy的数据结构和计算工具。
NumPy非常适合作为数据容器。对于数值型数据,NumPy数组在存储和处理数据时要比内置的Python数据结构高效得多。此外,由底层语言(比如C和Fortran)编写的库可以直接操作NumPy数组中的数据,无需将数据复制到其他内存中后再操作。因此,许多Python的数值计算工具要么使用NumPy数组作为主要的数据结构,要么可以与NumPy进行无缝交互操作。
pandas
pandas的作者就是本书作者Wes McKinney。
pandas兼具NumPy的高性能数组计算能力以及表格和关系型数据库(例如SQL)的灵活数据操作功能,提供了便捷的索引功能,可以完成重塑、切片、切块、连接和选取数据子集等操作。pandas具有如下功能:
- 带有标签轴的数据结构,支持自动或清晰的数据对齐。以防止由于数据未对齐,以及处理不同数据源且不同索引的数据,造成的常见错误。
- 集成时间序列功能。
- 用于同时处理时间序列数据和非时间序列数据的统一数据结构。
- 算术运算和规约[ 译者注:规约是计算机科学中降低计算复杂度的方法统称。]可以保存元数据。
- 灵活处理缺失数据。
- 合并和其他流行数据库(例如基于SQL的数据库)的关系型操作。
matplotlib
Matplotlib是最流行的用于绘制图表和其他二维数据可视化的Python库。
IPython和Jupyter
IPython是交互性的Python编程终端。IPython可以方便地访问系统的shell和文件系统,在许多场景中就无需在终端窗口和Python会话中来回切换。
Jupyter是一个更多语言交互计算工具,支持40多种编程语言。IPython现在可以作为Jupyter使用Python的内核(一种编程语言模式)。Jupyter notebook还可以编写Markdown和HTML文档。
SciPy
SciPy是专门解决科学计算中多种基本问题的包的集合。以下是其中的一些模块:scipy.integrate
:数值积分例程和微分方程求解器。scipy.linalg
:扩展了由numpy.linalg提供的线性代数例程和矩阵分解功能。scipy.optimize
:函数优化器(最小化器)以及求根算法。scipy.signal
:信号处理工具。scipy.sparse
:稀疏矩阵和稀疏线性系统求解器。scipy.special
:SPECFUN的封装器,SPECFUN是一个实现了许多常用数学函数(例如gamma函数)的FORTRAN库。scipy.stats
:标准连续和离散概率分布(密度函数、采样器、连续分布函数)、各种统计检验方法,以及各类描述性统计。
scikit-learn
scikit-learn是一个通用的机器学习工具包,它包括以下子模块:
- 分类:SVM、最近邻、随机森林、逻辑回归等。
- 回归:Lasso、岭回归等。
- 聚类:k-means、谱聚类等。
- 降维:PCA、特征选择、矩阵分解等。
- 模型选择:网格搜索、交叉验证、指标矩阵。
- 预处理:特征提取、正态化。
statsmodels
statsmodels是一个统计分析包,起源于斯坦福大学统计学教授Jonathan Taylor,他设计了多种流行于R语言的回归分析模型。Skipper Seabold和Josef Perktold在2010年正式创建了新的statsmodels项目,随后汇聚了大量的使用者和贡献者。受到R语言公式系统的启发,Nathaniel Smith开发了Patsy项目,提供了statsmodels的公式或模型的规范框架。
与scikit-learn相比,statsmodels包含经典统计学(主要是频度)和计量经济学的算法。它包括如下子模块:
- 回归模型:线性回归,广义线性模型,鲁棒线性模型,线性混合效应模型等。
- 方差分析(Analysis of variance,ANOVA)。
- 时间序列分析:AR,ARMA,ARIMA,VAR等模型。
- 非参数方法: 核密度估计,核回归。
- 统计模型结果可视化。
statsmodels更关注于统计推断,提供不确定性估计和参数p值参数检验。相反的,scikit-learn更注重预测。
5、准备开发环境
二、Python语法基础
1.解释器
运行Python解释器很便捷,在终端里输入python
就进入了Python解释器。如果要输出文本“Hello world”,则使用print
语句:
print("Hello world")
运行该脚本的方法是在终端里,执行命令python hello_world.py
2.IPython和Jupyter notebook
IPython:
IPython也是解释器,但增加了许多功能,最明显的是有行号。在终端里输入ipython
,进入IPython解释器。如果要在IPython中运行py
脚本,命令是%run hello_world.py
In [1]: %run hello_world.py
Hello world
In [2]:
IPython在代码美化上下了很大功夫,尤其是代码对齐、自动换行、面向对象,并且还有许多好用的命令。
在IPython中,打印对象不必使用print
命令,只需输入对象就成了。
In [1]: a = 5
In [2]: a
Out[2]: 5
再尝试一个复杂点的对象,使用NumPy生成一组随机数字:
In [5]: import numpy as np
In [6]: data = [np.random.standard_normal() for i in range(7)]
In [7]: data
Out[7]:
[-0.20470765948471295,
0.47894333805754824,
-0.5194387150567381,
-0.55573030434749,
1.9657805725027142,
1.3934058329729904,
0.09290787674371767]
和IPython配套的是Jupyter notebook。进入Jupyter notebook的方法是在终端中输入命令jupyter notebook,
Jupyter会自动打开默认的浏览器(除非用参数--no-browser
指定不打开浏览器)。或者,可以在启动notebook之后手动打开网页。
Tab补全
IPython具有Tab补全和代码提示功能
# 使用Tab补充变量名
In [1]: an_apple = 27
In [2]: an_example = 42
In [3]: an<Tab>
an_apple an_example any
# 使用Tab补充变量的方法
In [3]: b = [1, 2, 3]
In [4]: b.<Tab>
append() count() insert() reverse()
clear() extend() pop() sort()
copy() index() remove()
# 使用Tab补充模块的方法
In [1]: import datetime
In [2]: datetime.<Tab>
date MAXYEAR timedelta
datetime MINYEAR timezone
datetime_CAPI time tzinfo
Jupyter notebook中也支持补全。
自省
在对象前后使用问号(?),可以显示对象的信息:
In [1]: b = [1, 2, 3]
In [2]: b?
Type: list
String form: [1, 2, 3]
Length: 3
Docstring:
Built-in mutable sequence.
If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.
In [3]: print?
Docstring:
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
Type: builtin_function_or_method
3、Python语法基础
2.3.1 缩进
区别于其他编程语言,Python分行是使用缩进:
for x in array:
if x < pivot:
less.append(x)
else:
greater.append(x)
万物皆对象
Python中的标量、字符串、数据结构、函数、类、模块等都是对象,可以使用type(*)
方法查看其对象信息。
注释
Python在代码行开头使用#
进行注释。快捷方法是Ctrl+/
。
results = []
for line in file_handle:
# keep the empty lines for now
# if len(line) == 0:
# continue
results.append(line.replace("foo", "bar"))
函数和方法
可以用圆括号调用函数,传入零个或若干参数,可以选择将返回值赋值给一个变量,也可以不赋值:
result = f(x, y, z)
g()
几乎Python中的每个对象都有内部函数,称作方法(method),可以用来访问对象内部的内容。可以用下面的语句调用:
obj.some_method(x, y, z)
函数可以使用位置(positional)和关键字(keyword)参数:
result = f(a, b, c, d=5, e="foo")
变量和参数
当在Python中为变量(或命名)赋值,可以在等号右边创建对这个变量的引用。在使用中,考虑一个整数列表:
In [8]: a = [1, 2, 3]
假设将a
赋值给一个新变量b
:
In [9]: b = a
In [10]: b
Out[10]: [1, 2, 3]
在Python中,a
和b
实际上是引用了同一个对象,即原有列表[1, 2, 3]
(如图2-5所示的引用模型)。为了验证,可以先在a
中添加一个元素,然后检查b:
In [11]: a.append(4)
In [12]: b
Out[12]: [1, 2, 3, 4]
当你将对象作为参数传递给函数时,新的局域变量创建了对原始对象的引用,而不是复制。如果在函数里将一个新对象绑定到一个变量,这个操作不会影响到该函数(即函数圆括号以内的部分)以外“范围”的同名变量。因此,可以修改可变参数的内部值。假设有以下函数:
In [13]: def append_element(some_list, element):
....: some_list.append(element)
然后有:
In [14]: data = [1, 2, 3]
In [15]: append_element(data, 4)
In [16]: data
Out[16]: [1, 2, 3, 4]
动态引用,强类型
Python中的对象不涉及固有类型(在Java等语言中,当声明变量时,同时需要声明变量的类型,称其为变量的固有类型),通过引用,变量可以引用不同类型的对象。下面的代码是没有问题的:
In [17]: a = 5
In [18]: type(a)
Out[18]: int
In [19]: a = "foo"
In [20]: type(a)
Out[20]: str
变量是在特殊命名空间中的对象名;类型信息保存在对象自身中。但Python是“类型化语言”,如下所示:
In [21]: "5" + 5
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-21-7fe5aa79f268> in <module>
----> 1 "5" + 5
TypeError: can only concatenate str (not "int") to str
知道对象的类型很重要,最好能让函数可以处理多种类型的输入。使用isinstance
函数,可以检查对象是否为特定类型的实例:
In [26]: a = 5
In [27]: isinstance(a, int)
Out[27]: True
isinstance
可以接收包含类型的元组作为参数,检查对象类型是否在类型元组中:
In [28]: a = 5; b = 4.5
In [29]: isinstance(a, (int, float))
Out[29]: True
In [30]: isinstance(b, (int, float))
Out[30]: True
属性和方法
Python的对象通常都有属性(存储在对象“内部”的其他Python对象)和方法(与对象关联的函数,可以访问对象的内部数据)。二者可以通过obj.attribute_name
访问:
In [1]: a = "foo"
In [2]: a.<Press Tab>
capitalize() index() isspace() removesuffix() startswith()
casefold() isprintable() istitle() replace() strip()
center() isalnum() isupper() rfind() swapcase()
count() isalpha() join() rindex() title()
encode() isascii() ljust() rjust() translate()
endswith() isdecimal() lower() rpartition()
expandtabs() isdigit() lstrip() rsplit()
find() isidentifier() maketrans() rstrip()
format() islower() partition() split()
format_map() isnumeric() removeprefix() splitlines()
也可以用getattr
函数,通过名字访问属性和方法:
In [32]: getattr(a, "split")
Out[32]: <function str.split(sep=None, maxsplit=-1)>
鸭子类型
通常情况下,用户可能不关心对象的类型,只关心对象是否具有某些方法或特性。这通常称为鸭子类型(duck typing),源自“走起来像鸭子、叫起来像鸭子,那么它就是鸭子”的说法。例如,可以通过验证对象是否遵循迭代器协议(iterator protocol),验证它是否是可迭代的。对于许多对象,这意味着该对象有一个__iter__
“魔术方法”,但使用iter
函数来验证是更好的办法:
In [33]: def isiterable(obj):
....: try:
....: iter(obj)
....: return True
....: except TypeError: # not iterable
....: return False
对于字符串以及大多数Python集合类型,该函数会返回True
:
In [34]: isiterable("a string")
Out[34]: True
In [35]: isiterable([1, 2, 3])
Out[35]: True
In [36]: isiterable(5)
Out[36]: False
引入
在Python中,模块就是具有.py
扩展名且包含Python代码的文件。假设有以下模块:
# some_module.py
PI = 3.14159
def f(x):
return x + 2
def g(a, b):
return a + b
如果想从同路径下的另一个文件访问some_module.py
中定义的变量和函数,可以:
import some_module
result = some_module.f(5)
pi = some_module.PI
或者:
from some_module import g, PI
result = g(5, PI)
使用as
关键字,可以给引入的模块起不同的变量名:
import some_module as sm
from some_module import PI as pi, g as gf
r1 = sm.f(pi)
r2 = gf(6, pi)
二元运算符和比较运算符
Python中大部分二元数学运算和比较运算跟其他编程语言的数学语法很相似:
In [37]: 5 - 7
Out[37]: -2
In [38]: 12 + 21.5
Out[38]: 33.5
In [39]: 5 <= 2
Out[39]: False
表1 列出了所有可用的二元运算符。
要判断两个变量是否引用同一个对象,可以使用is
关键字。is not
可以判断两个对象是不同的:
In [40]: a = [1, 2, 3]
In [41]: b = a
In [42]: c = list(a)
In [43]: a is b
Out[43]: True
In [44]: a is not c
Out[44]: True
因为list
函数总是创建一个新的Python列表(即复制),我们可以断定c
是不同于a
的。is
关键字与==
运算符不同,如下所示:
In [45]: a == c
Out[45]: True
is
和is not
常用来判断变量是否为None
,因为None
的实例是唯一的:
In [46]: a = None
In [47]: a is None
Out[47]: True
可变与不可变对象
Python中的许多对象,例如列表、字典、NumPy数组,以及用户定义的类型(类),都是可变对象。这意味着可以修改这些对象或其包含的值:
In [48]: a_list = ["foo", 2, [4, 5]]
In [49]: a_list[2] = (3, 4)
In [50]: a_list
Out[50]: ['foo', 2, (3, 4)]
另外,例如字符串和元组,是不可变对象,即不能修改其内部数据:
2.3.2 标量类型
Python有为数不多的内置类型,用于处理数值数据、字符串、布尔值(True
或False
),以及日期时间。这些“单值”类型有时被称为标量类型(scalar type),本书中称其为标量(scalar)。表2列出了主要的标量。日期和时间处理会单独讨论,因为它们是标准库的datetime
模块提供的。
数值类型
Python的主要数值类型是int
和float
。int
可以存储任意大的数:
In [53]: ival = 17239871
In [54]: ival ** 6
Out[54]: 26254519291092456596965462913230729701102721
浮点数使用Python的float
类型表示。每个浮点数底层都是双精度(64位)的值。浮点数也可以用科学计数法表示:
In [55]: fval = 7.243
In [56]: fval2 = 6.78e-5
整数除法如果不能得到完整的整数,会自动将结果转换为浮点数:
In [57]: 3 / 2
Out[57]: 1.5
要使用C语言风格的整除(即如果不是完整的整数,则删除小数部分),可以使用底除运算符//
:
In [58]: 3 // 2
Out[58]: 1
字符串
可以用单引号'
或双引号"
创建字符串常量:
a = 'one way of writing a string'
b = "another way"
对于换行的多行字符串,可以使用三引号,'''
或"""
都行:
c = """
This is a longer string that
spans multiple lines
"""
字符串c
实际包含四行文本,"""
后和lines
后的换行符都是包含在字符串中的。可以用count
方法计算c
中的换行符:
In [60]: c.count("\n")
Out[60]: 3
Python的字符串是不可变的,不能修改字符串:
In [61]: a = "this is a string"
In [62]: a[10] = "f"
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-62-3b2d95f10db4> in <module>
----> 1 a[10] = "f"
TypeError: 'str' object does not support item assignment
如果需要修改字符串,必须用函数或方法创建一个新的字符串,例如使用字符串的replace
方法:
In [63]: b = a.replace("string", "longer string")
In [64]: b
Out[64]: 'this is a longer string'
使用str
函数,许多Python对象可以转化为字符串:
In [66]: a = 5.6
In [67]: s = str(a)
In [68]: print(s)
5.6
字符串是Unicode字符的序列,因此可以像其他序列一样处理,比如列表和元组:
In [69]: s = "python"
In [70]: list(s)
Out[70]: ['p', 'y', 't', 'h', 'o', 'n']
In [71]: s[:3]
Out[71]: 'pyt'
语法s[:3]
被称作切片(slicing),适用于多种Python序列。后面会更详细地介绍,本书中广泛使用了切片。
反斜杠\
是转义字符,用来表示特殊字符,比如换行符\n
或Unicode字符。要写一个包含反斜杠的字符串,需要进行转义:
In [72]: s = "12\\34"
In [73]: print(s)
12\34
如果字符串中包含许多反斜杠,但没有特殊字符,做起来就很麻烦。可以在字符串前面加一个前缀字符r
,表明该字符串是原生字符串:
In [74]: s = r"this\has\no\special\characters"
In [75]: s
Out[75]: 'this\\has\\no\\special\\characters'
r表示原生(raw)。
将两个字符串合并,会产生一个新的字符串:
In [76]: a = "this is the first half "
In [77]: b = "and this is the second half"
In [78]: a + b
Out[78]: 'this is the first half and this is the second half'
Python 3.6中引入了一个新功能,即f-字符串(f-string)(即格式化字符串(formatted string)的缩写),用其创建格式化字符串更为简单。要创建f-字符串,就在字符串的前方加上字符f。在字符串中,Python表达式需要放在尖括号中,用于将表达式替换为格式化字符串。
In [81]: amount = 10
In [82]: rate = 88.46
In [83]: currency = "Pesos"
In [84]: result = f"{amount} {currency} is worth US${amount / rate}"
可以在每个表达式的后面添加格式说明符,语法和之前的字符串模板相同:
In [85]: f"{amount} {currency} is worth US${amount / rate:.2f}"
Out[85]: '10 Pesos is worth US$0.11'
字节和Unicode
在当前的Python中(例如,Python 3.0及以上版本),Unicode成为了一级的字符串类型,可以更兼容地处理ASCII和非ASCII文本。在早期的Python版本中,字符串都是字节,不使用Unicode编码。假如知道字符的编码,可以将其转化为Unicode。看一个例子:
In [86]: val = "español"
In [87]: val
Out[87]: 'español'
可以用encode
方法将这个Unicode字符串转换为UTF-8字节:
In [88]: val_utf8 = val.encode("utf-8")
In [89]: val_utf8
Out[89]: b'espa\xc3\xb1ol'
In [90]: type(val_utf8)
Out[90]: bytes
假如知道一个字节对象的Unicode编码,用decode
方法可以解码:
In [91]: val_utf8.decode("utf-8")
Out[91]: 'español'
布尔值
Python中的两个布尔值写作True
和False
。比较运算和其他条件表达式的结果为True
和False
。布尔值可以与and
和or
关键字结合使用:
In [95]: True and True
Out[95]: True
In [96]: False or True
Out[96]: True
布尔值转换为数字时,False
变为0,True
变为1:
In [97]: int(False)
Out[97]: 0
In [98]: int(True)
Out[98]: 1
关键字not
会翻转布尔值,即True
变为False
,反之亦然:
In [99]: a = True
In [100]: b = False
In [101]: not a
Out[101]: False
In [102]: not b
Out[102]: True
类型转换
str
、bool
、int
和float
类型同时也是函数,可以将其他数据转换为对应的类型:
In [103]: s = "3.14159"
In [104]: fval = float(s)
In [105]: type(fval)
Out[105]: float
In [106]: int(fval)
Out[106]: 3
In [107]: bool(fval)
Out[107]: True
In [108]: bool(0)
Out[108]: False
None
None
是Python的空值类型。
In [109]: a = None
In [110]: a is None
Out[110]: True
In [111]: b = 5
In [112]: b is not None
Out[112]: True
None
也常常作为函数参数的默认值:
def add_and_maybe_multiply(a, b, c=None):
result = a + b
if c is not None:
result = result * c
return result
日期和时间
Python内置的datetime
模块提供了datetime
、date
和time
类型。datetime
类型结合了date
和time
二者存储的信息,是最常使用的:
In [113]: from datetime import datetime, date, time
In [114]: dt = datetime(2011, 10, 29, 20, 30, 21)
In [115]: dt.day
Out[115]: 29
In [116]: dt.minute
Out[116]: 30
对于datetime
实例,可以用date
和time
方法分别提取出date
和time
对象:
In [117]: dt.date()
Out[117]: datetime.date(2011, 10, 29)
In [118]: dt.time()
Out[118]: datetime.time(20, 30, 21)
strftime
方法可以将datetime
格式化为字符串:
In [119]: dt.strftime("%Y-%m-%d %H:%M")
Out[119]: '2011-10-29 20:30'
strptime
函数可以将字符串转换(解析)成datetime
对象:
In [120]: datetime.strptime("20091031", "%Y%m%d")
Out[120]: datetime.datetime(2009, 10, 31, 0, 0)
2.3.3 控制流
和其他编程语言一样,Python有若干内置的关键字进行条件逻辑、循环和其他控制流(control flow)操作。
if、elif和else
if
语句是最广为人知的控制流语句之一。它检查一个条件,如果为True
,则执行后面的语句:
x = -5
if x < 0:
print("It's negative")
if
语句后面可以跟一个或多个elif
代码块,如果所有条件都是False
时,还可以添加一个else
代码块:
if x < 0:
print("It's negative")
elif x == 0:
print("Equal to zero")
elif 0 < x < 5:
print("Positive but smaller than 5")
else:
print("Positive and larger than or equal to 5")
如果某个条件为True
,后面的elif代码块就不会被执行。当使用and
和or
时,复合条件语句是从左到右执行,并且会发生“短路”:
In [130]: a = 5; b = 7
In [131]: c = 8; d = 4
In [132]: if a < b or c > d:
.....: print("Made it")
Made it
for循环
for循环是在集合(例如列表或元组)或迭代器中进行迭代。可以用continue
使for循环提前进入下一次迭代,跳过剩下的部分。看下面这个例子,将列表中的整数相加,跳过None
值:
sequence = [1, 2, None, 4, None, 5]
total = 0
for value in sequence:
if value is None:
continue
total += value
可以用break
关键字跳出for循环。下面的代码将列表中各元素相加,直到遇到5:
sequence = [1, 2, 0, 4, 6, 5, 2, 1]
total_until_5 = 0
for value in sequence:
if value == 5:
break
total_until_5 += value
While循环
while循环指定了条件和代码快,当条件为False
或用break
退出循环,代码才会退出:
x = 256
total = 0
while x > 0:
if total > 500:
break
total += x
x = x // 2
pass
pass
是Python中的“无操作”(或“什么都不做”)语句。它用于不执行任何操作的代码块(或作为未完成代码的占位符);之所以需要它,是因为Python使用缩进界定代码块:
if x < 0:
print("negative!")
elif x == 0:
# TODO: put something smart here
pass
else:
print("positive!")
range
range
函数生成一个均匀分布的整数序列:
In [135]: range(10)
Out[135]: range(0, 10)
In [136]: list(range(10))
Out[136]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range
的三个参数是起点、终点、步进,其中步进值可以是负数:
In [137]: list(range(0, 20, 2))
Out[137]: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
In [138]: list(range(5, 0, -1))
Out[138]: [5, 4, 3, 2, 1]
2.4 总结
本章简要介绍了Python的基础语法概念,以及IPython和Jupyter编程环境。下一章中,我会介绍许多内置的数据类型、函数、输入-输出方法,这些内容将贯穿本书剩余章节。
三、Python的数据结构、函数和文件
本书第三章详细介绍了Python的内建数据结构、函数以及如何处理文件。这些是Python编程的基础,也是进行数据分析时不可或缺的工具。以下是本章内容的详细讲解:
3.1 数据结构
Python提供了多种内建的数据结构,如列表、字典、集合和元组,它们各自有不同的用途和特性。
列表
列表是Python中最常用的数据结构之一,它是一个有序的元素集合。
# 创建列表
my_list = [1, 2, 3]
# 添加元素
my_list.append(4) # 结果: [1, 2, 3, 4]
# 列表推导式
squares = [x**2 for x in range(10)] # 结果: [0, 1, 4, 9, ..., 81]
字典
字典存储键值对,每个键对应一个值。字典的键必须是唯一的。
# 创建字典
my_dict = {'apple': 'fruit', 'carrot': 'vegetable'}
# 访问字典
value = my_dict['apple'] # 结果: 'fruit'
# 添加或修改
my_dict['banana'] = 'fruit' # 添加新键值对
集合
集合是一个无序的不重复元素集。
# 创建集合
my_set = {1, 2, 3}
# 添加元素
my_set.add(4) # 结果: {1, 2, 3, 4}
# 集合运算
another_set = {3, 4, 5}
my_set.intersection(another_set) # 结果: {3, 4}
元组
元组是一个不可变的有序元素集合。
# 创建元组
my_tuple = (1, 2, 3)
# 访问元素
value = my_tuple[1] # 结果: 2
3.2 函数
函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段
# 定义函数
def add_numbers(x, y):
return x + y
# 调用函数
result = add_numbers(1, 2) # 结果: 3
3.3 文件处理
Python中的文件处理让我们可以读取和写入文件。
# 写入文件
with open('example.txt', 'w') as f:
f.write('Hello, world!')
# 读取文件
with open('example.txt', 'r') as f:
content = f.read() # 结果: 'Hello, world!'
四、NumPy基础:数组和向量化计算
本书第四章详细介绍了NumPy库的核心功能,包括多维数组对象(ndarray)的创建和操作,以及NumPy的向量化计算能力。
4.1 NumPy的ndarray:一种多维数组对象
NumPy的核心特性是ndarray对象,它是一个快速而灵活的大数据集容器。你可以对整个数组执行数学运算,而不需要编写循环。
创建ndarray
使用array
函数可以从列表等序列类型创建数组。
import numpy as np
# 从列表创建数组
data1 = [6, 7.5, 8, 0, 1]
arr1 = np.array(data1)
数组的数学运算
NumPy数组可以进行元素级的数学运算。
# 数组与标量之间的运算
arr = np.array([[1, 2, 3], [4, 5, 6]])
arr * arr # 元素相乘
arr - arr # 元素相减
基本的索引和切片
NumPy数组的索引和切片非常灵活。
# 索引
arr = np.arange(10)
arr[5] # 获取单个元素
# 切片
arr[5:8] # 获取子数组
4.2 NumPy数组的数据类型
NumPy的数组是同质的,也就是说数组中的所有元素类型都是相同的。当创建数组时,NumPy会尝试猜测一个合适的数据类型,但是函数也允许你指定数据类型。
指定数据类型
arr1 = np.array([1, 2, 3], dtype=np.float64) # 创建一个浮点型数组
arr2 = np.array([1, 2, 3], dtype=np.int32) # 创建一个整型数组
4.3 NumPy的广播机制
广播是NumPy的一种强大机制,它允许不同形状的数组进行数学运算。
广播示例
arr = np.array([[1, 2, 3], [4, 5, 6]])
scalar = 10
arr * scalar # 数组与标量相乘,标量会广播到数组的每个元素
4.4 通用函数:快速的元素级数组函数
NumPy提供了通用函数(ufunc),这些函数是对ndarray中的数据执行元素级运算的函数。
使用通用函数
arr = np.arange(10)
np.sqrt(arr) # 计算每个元素的平方根
np.exp(arr) # 计算每个元素的指数
五、pandas入门
5.1 pandas的数据结构介绍
pandas有两个主要的数据结构:Series和DataFrame:
Series
Series是一种一维数组型对象,它包含了一个值序列(与NumPy中的类型相似),并且包含了数据标签,称为索引(index)。
import pandas as pd
from pandas import Series, DataFrame
# 创建一个Series
obj = Series([4, 7, -5, 3])
# Series的索引和值
obj.values # 返回值
obj.index # 返回索引
你可以通过索引的方式选取Series中的单个或一组值:
# 使用标签索引
obj2 = Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
obj2['a'] # 输出: -5
Series的字符串表现形式为:索引在左边,值在右边。如果没有为数据指定索引,会自动创建一个0到N-1(N为数据的长度)的整数型索引。你可以通过Series的values
和index
属性获取其数组表示形式和索引对象。
DataFrame
DataFrame是一个表格型的数据结构,它包含一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。
import pandas as pd
from pandas import DataFrame
# 创建DataFrame的数据
data = {
'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
'year': [2000, 2001, 2002, 2001, 2002],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9]
}
# 创建DataFrame
frame = DataFrame(data)
DataFrame的列操作
对DataFrame进行列选择、列修改、列添加和列删除等操作。
# 选择列
frame['state'] # 返回'state'列
# 修改列
frame['debt'] = 16.5 # 添加一个新列'debt'
# 删除列
del frame['debt'] # 删除'debt'列
5.2 基本功能
pandas提供了许多基本功能,以便对Series和DataFrame进行操作。
重新索引
使用reindex
可以创建一个新对象,它的数据符合新的索引。
obj = Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
如果某个索引值当前不存在,就引入缺失值。
丢弃指定轴上的项
使用drop
方法可以删除Series和DataFrame中的条目。
obj = Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
new_obj = obj.drop('c')
对于DataFrame,可以删除任意轴上的索引值。
5.3 索引、选择与过滤
DataFrame的索引选项比Series更为复杂,因为你可以从行和列两个维度进行选择
使用标签选择数据
你可以使用.loc
方法通过标签来选择数据。
import pandas as pd
from pandas import DataFrame
df = DataFrame(np.arange(16).reshape((4, 4)),
index=['Ohio', 'Colorado', 'Utah', 'New York'],
columns=['one', 'two', 'three', 'four'])
# 用标签选择数据
df.loc['Colorado', ['two', 'three']]
使用位置选择数据
.iloc
方法允许你通过行和列的位置来选择数据。
# 用位置选择数据
df.iloc[2, [3, 0, 1]]
df.iloc[[1, 2], [3, 0, 1]]
使用切片
切片不仅可以用于选择行,也可以选择列。
# 行切片
df['Colorado':'Utah']
# 列切片
df.iloc[:, :3][df.three > 5]
布尔型索引
布尔型索引可以让你根据条件选择数据。
# 布尔型索引
df[df['three'] > 5]
使用isin
进行过滤
isin
方法是一个非常有用的方法,它可以帮助你过滤数据框中的数据。
# 使用isin进行过滤
df[df['one'].isin([0, 4])]
六、数据加载、存储与文件格式
这一章节主要介绍了如何在pandas中加载、存储和处理文件格式的数据。
6.1 读写文本格式的数据
pandas提供了一些用于将表格型数据读取为DataFrame对象的函数,如read_csv
和read_table
。
import pandas as pd
# 使用read_csv读取数据
df = pd.read_csv('examples/ex1.csv')
# 使用read_table,并指定分隔符
df = pd.read_table('examples/ex1.csv', sep=',')
6.2 二进制数据格式
可以使用Python内置的pickle序列化来保存数据,这是实现数据的高效二进制格式存储的一种简单方式。
# 将数据以pickle格式保存到磁盘
frame.to_pickle('examples/frame_pickle')
# 读取pickle数据
pd.read_pickle('examples/frame_pickle')
6.3 使用HDF5格式
HDF5是一种存储大规模科学数组数据的非常好的文件格式,它支持即时压缩以及高效的IO操作。
# 存储到HDF5格式
store = pd.HDFStore('mydata.h5')
store['obj1'] = frame
store['obj1_col'] = frame['a']
store.close()
# 读取HDF5格式
pd.read_hdf('mydata.h5', 'obj1')
6.4 读取Microsoft Excel文件
pandas可以读取Excel文件中的数据。
# 读取Excel文件
xlsx = pd.ExcelFile('examples/ex1.xlsx')
pd.read_excel(xlsx, 'Sheet1')
七、数据清洗和准备
这一章节涉及到数据分析过程中的数据准备工作,包括清理、转换以及重塑数据。
7.1 处理缺失数据
pandas中的许多方法都可以处理缺失数据。
import numpy as np
# pandas对象的描述性统计默认不包括缺失数据
string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
string_data.isnull()
7.2 数据转换
包括移除重复数据、替换值、重命名轴索引等操作。
# 移除重复数据
data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'], 'k2': [1, 1, 2, 3, 3, 4, 4]})
data.duplicated()
data.drop_duplicates()
7.3 字符串操作
pandas提供了一系列字符串处理方法,方便对字符串数据进行操作。
# 字符串操作
val = 'a,b, guido'
val.split(',')
八、数据规整:连接、联合和重塑
这一章节主要介绍了如何在pandas中进行数据的连接(merge)、联合(join)以及重塑(reshaping)。
8.1 分层索引
分层索引允许你在一个轴向上拥有多个(两个或两个以上)索引层级。这为数据提供了多维度的形式。
import pandas as pd
import numpy as np
data = pd.Series(np.random.randn(9),
index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],
[1, 2, 3, 1, 3, 1, 2, 2, 3]])
使用unstack
方法可以将Series重塑为DataFrame。
data.unstack()
相反,stack
方法可以将DataFrame重塑为Series。
8.2 数据库风格的DataFrame连接
使用merge
或join
操作可以将不同的DataFrame按照一定的逻辑规则连接起来。
df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1': range(7)})
df2 = pd.DataFrame({'key': ['a', 'b', 'd'],
'data2': range(3)})
pd.merge(df1, df2, on='key')
8.3 重塑和透视
pivot
方法可以将数据从“长格式”透视为“宽格式”。
df = pd.DataFrame({'key': ['foo', 'bar', 'baz'],
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9]})
melted = pd.melt(df, ['key'])
reshaped = melted.pivot('key', 'variable', 'value')
九、绘图和可视化
这一章节介绍了如何使用matplotlib和pandas进行数据的绘图和可视化。
9.1 matplotlib API入门
matplotlib是一个强大的Python绘图库,用于创建各种静态、动态、交互式的图表。
import matplotlib.pyplot as plt
data = np.arange(10)
plt.plot(data)
9.2 使用pandas和seaborn绘图
pandas提供了便捷的方法直接从DataFrame和Series绘图。seaborn是基于matplotlib的高级绘图库,提供了更多样化的图表类型和美观的绘图风格。
import seaborn as sns
tips = sns.load_dataset('tips')
sns.barplot(x='day', y='total_bill', data=tips)
十、数据聚合与分组操作
这一章节主要介绍了如何在pandas中进行数据的聚合和分组,这是数据分析中的一个核心环节
10.1 GroupBy机制
使用groupby
方法可以对数据进行分组,并对每个分组应用聚合函数。
import pandas as pd
import numpy as np
df = pd.DataFrame({'key1': ['a', 'a', 'b', 'b', 'a'],
'key2': ['one', 'two', 'one', 'two', 'one'],
'data1': np.random.randn(5),
'data2': np.random.randn(5)})
grouped = df['data1'].groupby(df['key1'])
grouped.mean()
10.2 数据聚合
聚合操作,如sum
、mean
、std
,是对数据集进行汇总的常用方法。
# 使用自定义聚合函数
def peak_to_peak(arr):
return arr.max() - arr.min()
grouped.agg(peak_to_peak)
10.3 分组级运算和转换
transform
和apply
方法能够执行更复杂的分组级运算。
# 标准化数据
def normalize(x):
return (x - x.mean()) / x.std()
grouped.transform(normalize)
十一、时间序列
这一章节涉及时间序列数据的处理,这是金融分析中常见的数据类型。
11.1 日期和时间数据类型及工具
pandas支持多种与时间相关的数据类型和工具。
from datetime import datetime
from pandas import Timestamp
# 当前时间
now = datetime.now()
# pandas时间戳
stamp = Timestamp('2011-01-03')
11.2 时间序列基础
pandas的DatetimeIndex
类型提供了许多时间序列处理的基础功能。
dates = [datetime(2011, 1, 2), datetime(2011, 1, 5),
datetime(2011, 1, 7), datetime(2011, 1, 8),
datetime(2011, 1, 10), datetime(2011, 1, 12)]
ts = pd.Series(np.random.randn(6), index=dates)
11.3 日期范围、频率和移动
使用date_range
可以生成指定频率的日期范围。
index = pd.date_range('2012-04-01', '2012-06-01')
十二、pandas高级应用
12.1 分类数据
pandas的分类类型用于保存使用整数分类表示法的数据。这种表示法提高了性能和内存的使用率。
import pandas as pd
import numpy as np
values = pd.Series(['apple', 'orange', 'apple', 'apple'] * 2)
pd.unique(values)
pd.value_counts(values)
分类对象有categories
和codes
属性,可以通过astype
方法将数据转换为分类类型。
df = pd.DataFrame({'fruit': values, 'basket_id': np.arange(len(values))})
fruit_cat = df['fruit'].astype('category')
c = fruit_cat.values
c.categories
c.codes
12.2 GroupBy高级应用
GroupBy的transform
方法与apply
类似,但对使用的函数有一定限制。它可以产生向分组形状广播标量值,也可以产生一个和输入组形状相同的对象。
df = pd.DataFrame({'key': ['a', 'b', 'c'] * 4, 'value': np.arange(12.)})
g = df.groupby('key').value
g.transform(lambda x: x.mean())
内置的聚合函数,如mean
或sum
,通常比apply
函数快,也比transform
快。
12.3 链式编程技术
链式编程或方法链是一种使代码简洁的技术。在pandas中,你可以将多个方法调用链接起来,这样可以避免创建中间变量。
十三、Python建模库介绍
13.1 pandas与模型代码的接口
模型开发通常包括使用pandas进行数据加载和清洗,然后切换到建模库进行建模。pandas与其他分析库通常是通过NumPy的数组联系起来的。
data = pd.DataFrame({
'x0': [1, 2, 3, 4, 5],
'x1': [0.01, -0.01, 0.25, -4.1, 0.],
'y': [-1.5, 0., 3.6, 1.3, -2.]
})
data.values
将DataFrame转换为NumPy数组,可以使用.values
属性。
13.2 用Patsy创建模型描述
Patsy是Python的一个库,使用字符串“公式语法”描述统计模型,适合描述statsmodels的线性模型。
import patsy
y, X = patsy.dmatrices('y ~ x0 + x1', data)
13.3 statsmodels介绍
statsmodels是Python的一个库,用于拟合多种统计模型,进行统计测试和数据探索。
import statsmodels.api as sm
import statsmodels.formula.api as smf
results = smf.ols('y ~ x0 + x1', data=data).fit()
results.summary()
13.4 scikit-learn介绍
scikit-learn是Python的一个库,支持简单和复杂的机器学习算法。
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X, y)
总结
《利用Python进行数据分析》这本书是数据分析领域的经典入门读物,全书共十三章,内容涵盖了使用Python进行数据分析的全过程,从基础的Python编程知识到高级的数据处理技巧。接下来对全书内容进行一个简短总结:
- 准备工作:介绍了为什么选择Python进行数据分析,以及数据分析相关的Python库。
- Python语法基础:涉及Python的基本语法,包括变量、数据结构、控制流等。
- Python的数据结构、函数和文件:讲解了Python内建的数据结构,如列表和字典,以及函数的定义和文件操作。
- NumPy基础:介绍了NumPy库,重点是多维数组对象及其运算。
- pandas入门:详细介绍了pandas库的核心数据结构Series和DataFrame,以及基本的数据操作。
- 数据加载、存储与文件格式:讨论了如何读取和写入各种文件格式的数据。
- 数据清洗和准备:包括处理缺失数据、数据转换、字符串操作等。
- 数据规整:涉及数据连接、联合以及重塑。
- 绘图和可视化:介绍了matplotlib和seaborn等库的绘图工具。
- 数据聚合与分组操作:讲解了GroupBy机制和数据聚合的技术。
- 时间序列:专门讨论了时间序列数据的处理方法。
- pandas高级应用:深入探讨了pandas的高级功能,如分类数据、GroupBy高级应用等。
- Python建模库介绍:介绍了Patsy、statsmodels和scikit-learn等建模库。
这本书不仅提供了丰富的示例代码,还有详细的解释和操作指南,适合初学者和有一定基础的读者深入学习Python数据分析!