Python 秘籍手册(一)
一、字符串和文本
自从计算的早期以来,计算中使用的数据存储在基本的文本文件中。任何编写过 shell 脚本的人都非常清楚,Unix 系统及其工具是基于这样一种假设构建的,即处理文本将是程序的大部分工作。Python 也不例外;它提供了几个编程元素来帮助完成基本的文本处理任务。
首先,让我们注意一下 Python 是如何存储字符串的。字符串是不可变的列表,所以它们不能被改变。对字符串内容的任何更改都需要复制到内存中的新位置。当您试图优化代码的任何文本处理部分时,您必须始终牢记这一点。
1-1.串联字符串
问题
你想用一些更小的字符串来构建字符串。这个过程称为串联。
解决办法
构建字符串最简单的方法是使用+操作符。在这种情况下,字符串被放在一起,并返回完整的新字符串。
它是如何工作的
清单 1-1 展示了一个例子。
>>> new_str = "hello " + "world"
>>> print(new_str)
hello world
Listing 1-1.Basic Concatenation
这段代码返回字符串“hello world”。如果希望两个单词之间有一个空格,需要显式地给其中一个字符串添加一个空格。
您也可以从一个较小字符串的多个副本创建字符串。这是通过使用*操作符并乘以您想要的副本数来完成的。参见清单 1-2 中的示例。
>>> new_str = "Hello" * 3
>>> print(new_str)
HelloHelloHello
Listing 1-2.
Multiplicative Concatenation
这将返回字符串“HelloHelloHello”。
当您只处理字符串时,这两个操作符工作得很好。如果您想使用其他数据类型,您可以使用上面的例子,首先将您的数据传递到函数str()中。通过这种方式,您可以将数字添加到构造的字符串中。清单 1-3 中显示了一个例子。
>>> New_str = "Count = " + str(42)
>>> print(New_str)
Count = 42
Listing 1-3.Concatenating Non-Strings
1-2.比较字符串
问题
您想要比较字符串,检查两个字符串是否具有相同的值,或者检查两个名称是否指向同一个 string 对象。
解决办法
有两种方法进行比较,使用is或使用==。第一种是测试两个变量名是否引用同一个对象的方法,第二种是比较每个变量的实际值的方法。
它是如何工作的
要测试相同的文本是否存储在两个独立的字符串中,使用类似清单 1-4 中的代码。
str1 = "Hello"
str2 = "World"
if str1 == str2:
print("The strings are equal")
else:
print("The strings are not equal")
Listing 1-4.Comparing Strings
此代码返回“字符串不相等”。您可以使用任何常用的比较运算符,如!=、<或>.
Note
当进行大于或小于比较时,字符串是逐字母比较的。另外,Python 对待大写字母和小写字母的方式不同。大写字母在小写字母之前,所以 Z 在 a 之前。
1-3.搜索子字符串
问题
你想在一个字符串对象中搜索一个子串。
解决办法
您可以使用in操作符来检测子字符串是否存在。您可以通过使用 string 对象的find()方法来定位该子字符串的起始索引。
它是如何工作的
在 Python 中,有一个名为in的多态操作符,可以用来查看一个数据元素是否存在于一个更大的数据元素集合中。当您需要查看一个子字符串是否存在于另一个 string 对象中时,这也是可行的。清单 1-5 中给出了用法。
>>> Str1 = 'This is a string'
>>> 'is' in Str1
True
>>> 'me' in Str1
False
Listing 1-5.Looking for a Substring
如您所见,它返回一个布尔值,告诉您是否找到了子串。如果需要查找子字符串的位置,请使用 string 对象的 find 方法。使用上面的代码,您可以使用
>>> Str1.find('is')
2
>>> Str1.find('me')
-1
Listing 1-6.Finding the Index of a Substring
这段代码返回子字符串第一个实例的索引。如果要查找其他实例,可以包含一个开始和/或结束索引值,以便进行搜索。因此,要找到第二个实例is,使用清单 1-7 中的代码。
>>> Str1.find('is', 3)
5
Listing 1-7.Finding a Substring Beyond the First One
1-4.获取子字符串
问题
你需要从一个字符串对象中获取一个子串。
解决办法
一旦找到子字符串的索引,就可以通过复制原始字符串的一部分来获得它。这是通过使用切片符号从字符串中获取元素来实现的。
它是如何工作的
切片由开始索引和结束索引定义。要获取字符串中的第一个单词,您可以使用清单 1-8 中显示的任一选项。
>>> Str2 = 'One two three'
>>> Str2[0:2]
On
>>> Str2[:2]
On
>>> Str2[8:]
three
Listing 1-8.Slice Notation
Note
切片适用于 Python 中的所有列表。您还可以使用负索引值来向后计数,而不是向前计数。
1-5.替换文本匹配
问题
你需要用新的内容替换字符串的一部分。
解决办法
因为 Python 字符串是不可变的,所以替换子字符串需要将原始字符串切碎,然后将所有内容重新连接成一个新字符串。
它是如何工作的
让我们使用上述切片和拼接的例子,并将它们放在一起,如清单 1-9 所示。
>>> str1 = "Here are a string"
>>> corrected_str1 = str1[:5] + "is" + str1[7:]
>>> print(corrected_str1)
Here is a string
Listing 1-9.Manual String Replacement
string 对象包含一个名为replace()的方法,该方法也可以用来替换一个或多个子字符串实例。您需要提交旧的子串、要替换的新子串以及要替换的实例数。参见清单 1-10 。
>>> corrected_str1 = str1.replace("are", "is", 1)
Listing 1-10.Using the replace() Function
如果省略 count,此方法将替换字符串中的每个实例。
1-6.反转字符串
问题
你需要反转一个字符串对象的内容。
解决办法
反转字符串可以通过使用切片符号以相反的顺序选择出单个字符来完成。
它是如何工作的
Python 包含了扩展切片的概念,它包含第三个参数来定义在列表中移动时的步幅长度。如果这个步幅长度是负的,您是在告诉 Python 向后遍历列表。反转字符串的一行程序类似于清单 1-11 中的代码。
>>> str1 = "Hello World"
>>> str2 = str1[::-1]
>>> print(str2)
dlrow olleH
Listing 1-11.Reversing a String with Slices
因为这是一种特殊类型的切片,所以可以用它来获取一个子串,并在一个命令中全部反转。
1-7.修剪空白
问题
您需要从用户输入中删除空白。
解决办法
string 对象的strip()方法可以用来从字符串中删除任何多余的空白字符。
它是如何工作的
当您的代码需要接受来自用户的文本输入时,您通常需要能够修剪掉可能存在于字符串开头或结尾的任何空白字符。如果您希望简单地删除字符串开头和结尾的所有空白字符,您可以使用strip()方法。参见清单 1-12 。
>>> str1 = "Space"
>>> str2 = str1.strip()
>>> print(str2)
Space
Listing 1-12.Stripping Whitespace
如果您想从字符串的开头和结尾去掉某个特定的字符,您可以将它作为参数传递给方法。您可以分别使用方法lstrip()或rstrip()从开头或结尾去掉不需要的字符。
1-8.改变大小写
问题
您需要将字符的大小写设置为全部大写或全部小写。
解决办法
方法可以对内容执行大小写更改。
它是如何工作的
处理用户输入时出现的另一个问题是,您可能需要将所有字符设置为大写或小写。这通常是对放入数据库的信息进行的,以简化两个值之间的比较。这样可以避免前面提到的大写和小写字符被区别对待的问题。在这两种情况下,这都是通过 string 对象提供的方法来完成的。参见清单 1-13 。
>>> str1 = "Hello World"
>>> print(str1.lower())
hello world
>>> print(str1.upper())
HELLO WORLD
Listing 1-13.Changing the Case of a String
Note
你可以用方法capitalize()将一个字符串大写。
1-9.转换为数字
问题
您需要将用户输入的数字转换为数字数据类型。
解决办法
有一些转换函数可以将字符串转换成其他数据类型。对于数字,有int()、float()、long()和complex()函数。
它是如何工作的
这些函数接受一个字符串,并返回您所请求类型的数字。该字符串应该与直接在 Python 中输入的数字文字具有相同的形式。如果字符串与请求的类型转换的预期格式不匹配,将会出现错误。
默认的基数是 10。您可以输入不同的基数,以便创建不同的数字。例如,如果您正在输入十六进制数字,您可以使用清单 1-14 中的代码。
>>> hex1 = int("ae", 16)
>>> hex1
174
Listing 1-14.Type Cast Functions
可能的基数是 2 到 36。
1-10.迭代字符串中的字符
问题
您需要遍历字符串对象的字符,并对每个字符应用一些处理。
解决办法
您可以使用一个for循环迭代每个字符。
它是如何工作的
如果您需要处理给定字符串的每个字符,那么您需要能够遍历该字符串。您可以构建一个简单的循环,使用索引并从列表中提取每个元素。参见清单 1-15 。
str1 = "0123456789"
for i in range(10):
print(str1[i], " and ")
Listing 1-15.Iterating Over a String
此代码返回文本“0 和 1 以及 2 和 3”。更 Pythonic 化的方法是使用迭代器。令人高兴的是,列表自动支持迭代器方法。代码可以更改为清单 1-16 中的代码。
for i in str1:
print(i, " and ")
Listing 1-16.Using an Iterator
1-11.文本统计
问题
您需要找到字符串对象的统计数据。
解决办法
如果您对查看给定字符串的总体统计信息感兴趣,可以使用几种不同的方法和函数来收集这类信息。
它是如何工作的
最基本的统计是字符串中的字符数,由函数len()给出。您可以使用函数min()和max()找到最小和最大字符值。要获得更详细的信息,您可以使用count()方法来查看某些字符在字符串中出现了多少次。见清单 1-17 。
>>> str1 = "Hello world"
>>> len(str1)
11
>>> min(str1)
' '
>>> max(str1)
'w'
>>> str1.count('o')
2
Listing 1-17.String Statistics
1-12.编码 Unicode
问题
您需要将字符串编码为 Unicode。
解决办法
Python 曾经有一个unicode数据类型用于存储 Unicode 编码的字符串。从版本 3 开始,Python 字符串文字默认存储为 Unicode 字符串。
它是如何工作的
如果您使用的是 Python 3,标准字符串对象已经存储为 Unicode。如果您使用的是 Python 2,您可以通过使用构造函数或定义 Unicode 文字来创建它们。参见清单 1-18 。
>>> ustr1 = unicode("Hello")
>>> ustr2 = u’Hello’
>>> ustr1 == ustr2
True
Listing 1-18.Using Unicode
Unicode 值实际上存储为整数。您可以将这些值编码成特定的编码字符串。举个例子,
ustr1.encode("utf-8")
给你一个编码为 UTF 8 的新字符串。您还可以使用decode()方法将这个字符串解码回普通的 Unicode 字符串。
1-13.翻译
问题
你需要翻译字符串的内容。
解决办法
您可以将翻译映射应用于整个字符串。如果你想完成几个替换,这是很方便的。
它是如何工作的
您可以使用字符串的translate()方法来应用这种映射。虽然您可以手动创建转换表,但是 string 数据类型包含一个名为maketrans()的帮助函数,它创建一个转换表,将第一个参数中的每个字符映射到第二个参数中相同位置的字符。如清单 1-19 所示。
>>> str1 = "Hello world"
>>> translate_table = str1.maketrans("abcd", "efgh")
>>> str1.translate(translate_table)
Hello worlh
Listing 1-19.
Translating Strings
translate方法可选地接受第二个参数,该参数包含一组要从给定字符串中删除并执行翻译的字符。如果您希望能够从字符串中删除一组字符,可以通过将转换表设置为 None 来实现。这样,你就可以用清单 1-20 中的代码去掉字符 l 和 w。
>>> str1.translate(str.maketrans({'l':None,'W':None}))
Heo ord
Listing 1-20.Using translate to Delete Characters
二、数字、日期和时间
电子计算机的最初目的是计算物理问题的答案。这取决于数字的有效使用和正确处理。这仍然消耗了普通计算机程序的大部分 CPU 周期。除了处理数字,在很多情况下,您还需要能够处理日期和时间。当您需要比较不同时区或闰日的年份时,这就有点麻烦了。
2-1.创建整数
问题
你需要创建整数。
解决办法
创建整数有几种方法。最简单的是简单地写一个整数。还有一些创建函数,允许你用任意的基数来创建任意的整数。在 Python 中,整数是无限范围内的数字,受限于可用的内存量。
它是如何工作的
清单 2-1 展示了一个例子。
>>> a = 123456789
>>> a.bit_length()
27
Listing 2-1.Integer Literals
这段代码创建了一个新的 integer 对象,通过变量a来访问。您可以使用方法bit_length()来查看使用了多少位来存储这个整数。整数是不可变的,所以如果你试图通过数学运算来改变它们,你最终会得到一个新的整数。
在清单 2-2 中,您可以看到如何使用基数 10 以外的东西来创建整数。
>>> b = int('110',2)
>>> b
6
Listing 2-2.Integer Object Instantiation
2-2.创建浮点
问题
你需要创建浮点数。
解决办法
与整数一样,您可以创建浮点文字,也可以使用函数float()。
它是如何工作的
使用浮点文字时,需要包含小数点或指数。参见清单 2-3 中的一些例子。
>>> b = 1.
>>> b
1.0
>>> c = 1e2
>>> c
100.0
Listing 2-3.Using Float Literals
浮点的实际存储表示依赖于实现,并且在不同的架构上是不同的。
2-3.将浮点数舍入为整数
问题
你想把浮点数转换成整数。
解决办法
有两种方法可以将浮点数转换成整数。您可以将数字截断到小数点之前的部分,也可以将数字四舍五入到最接近的整数值。
它是如何工作的
可以使用和上面提到的相同的int()函数。当您将一个浮点作为输入传递给它时,它会将数字向 0 截断。参见清单 2-4 。
>>> c = 1234.567
>>> int(c)
1234
Listing 2-4.Truncating Floating Point Numbers
在math模块中有一个函数可以将浮点数截断成整数部分。它叫做math.trunc(),清单 2-5 显示了如何使用它。
>>> import math
>>> math.trunc(c)
1234
Listing 2-5.Using math.trunc()
如你所见,它基本上去掉了小数部分,给你剩下的部分。如果您需要得到一个取整的值,查看清单 2-6 。
>>> round(c)
1235
Listing 2-6.Rounding Floating Point Numbers
如果有必要,您可以四舍五入到某个固定的小数位数,如清单 2-7 所示。
>>> round(c, 1)
1234.6
Listing 2-7.Rounding to One Decimal Place
2-4.格式化数字
问题
你需要得到一个数字的字符串表示。
解决办法
内置函数format()可以用来输出数字的各种字符串表示。
它是如何工作的
format函数采用一种格式规范,定义如何将数字转换成字符串。这种字符串的一般格式如清单 2-8 所示。
[sign][width][.precision][type]
Listing 2-8.Format Specification
如果符号是+,你的数字会一直有一个前导符号。如果是-,只有数字是负数才会加前导符号。如果您使用空格,如果数字是负数,您将得到一个前导减号,如果数字是正数,您将得到一个前导空格。
宽度值定义了总数的最小大小。如果宽度大于数字的大小,您将获得前导空格来填充到正确的大小。精度定义了在显示数字时应该使用多少个小数位。当然,这只对浮点数有效。
该类型可以是几种不同的可能性之一:
| 类型 | 意义 | | --- | --- | | `B` | 二进制格式 | | `D` | 十进制整数 | | `O` | 八进制整数 | | `x` | 十六进制格式,a-f 使用小写字母 | | `X` | 十六进制格式,A-F 使用大写字母 | | `n` | 这与`d`相同 | | 没有人 | 这与`d`相同 |对于浮点数,有一个完整的其他类型可供选择:
| 类型 | 意义 | | --- | --- | | `e` | 指数格式,用`e`标记指数。默认精度为 6。 | | `E` | 指数格式,用`E`标记指数。默认精度为 6。 | | `f` | 默认精度为 6 的定点。 | | `F` | 与`f`相同,只是 nan 转换为 NAN,inf 转换为 INF。 | | `g` | 常规格式,其中数字被舍入到给定的精度。如果数字太大,则以指数形式显示,用`e`标记指数。默认精度为 6。 | | `G` | 这与`g`相同,只是指数标有`E`。 | | `n` | 号码。这与`g`相同,只是它使用当前的区域设置来获取正确的分隔符。 | | `%` | 百分比,其中数字乘以 100,以`f`格式显示,带有百分比符号。 | | 没有人 | 这个和`g`一样。 |示例如清单 2-9 所示。
>>> format(c)
'1234.567'
>>> format(c, '+.2f')
'+1234.57'
>>> format(.25, '%')
'25.000000%'
Listing 2-9.Formatting Numbers
2-5.使用任意精度的数字
问题
您需要处理任意大小和精度的浮点数。
解决办法
Python 提供了一个名为decimal的模块来处理用户定义的精度数。
它是如何工作的
decimal模块提供了一个名为Decimal的新对象,它将浮点数的存储和操作从底层架构中抽象出来。见清单 2-10 。
>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>> getcontext().prec = 10
Listing 2-10.Importing the Decimal Object and Setting Precision
清单 2-10 显示了如何设置decimal模块中使用的精度。清单 2-11 展示了如何使用Decimal类。
>>> a = Decimal(10.5)
>>> a
Decimal('10.5')
>>> 2*a
Decimal('21.0')
Listing 2-11.Using the Decimal Class
2-6.生成随机数
问题
为了在代码中引入随机化,您需要生成随机数。
解决办法
Python 中的random模块提供了为各种分布生成伪随机数的函数。
它是如何工作的
在没有非常专业的硬件的情况下,所有随机数都是使用一类称为伪随机数生成器(PRNGs)的函数来生成的。可用的更好的算法之一是称为 Mersenne twisters 的函数类。Python random模块使用 Mersenne twisters 来生成随机数。使用当前系统时间作为种子来初始化生成器,参见清单 2-12 。
>>> import random
>>> random.seed()
Listing 2-12.Initializing the Random Module
您可以通过调用random.random()获得 0.0 到 1.0 之间的下一个随机浮点数,如清单 2-13 所示。
>>> random.random()
0.35060766413719124
Listing 2-13.Getting the Next Random Number
您还可以通过使用random.choice()函数在许多可能性中进行随机选择,如清单 2-14 所示。
>>> items = [1, 2, 3, 4]
>>> random.choice(items)
3
Listing 2-14.Making a Random Choice
2-7.获取当前日期和时间
问题
您需要从系统中获取当前日期和时间。
解决办法
Python 提供了一个名为datetime的模块,该模块提供了处理日期、时间和时区的对象。
它是如何工作的
datetime模块提供了一个datetime类,包含所有的日期和时间信息,以及任何时区信息。清单 2-15 展示了如何获取当前日期和时间。
>>> import datetime
>>> curr_datetime = datetime.datetime.now()
>>> curr_datetime.year
2016
>>> curr_datetime.weekday()
2
Listing 2-15.Getting the Current Date and Time
在datetime对象中有许多可用的帮助函数和属性,允许您以任何需要的方式解析日期和时间信息。
如果您只需要日期或时间部分,那么可以使用date和time类。有datetime类的帮助器方法,即date()和time(),它们返回适当类型的对象。清单 2-16 展示了如何获取当前日期和时间的time和date对象。
>>> import datetime
>>> curr_datetime = datetime.datetime.now()
>>> curr_date = curr_datetime.date()
>>> curr_time = curr_datetime.time()
Listing 2-16.Getting Objects for the Current Date and Time
2-8.计算日期/时间差异
问题
您需要能够找出两个时间和/或两个日期之间的差异。
解决办法
Python 提供了作为datetime模块一部分的timedelta对象。
它是如何工作的
您可以创建一个代表某个日期或时间跨度的timedelta对象。如果你有两个datetime对象,你可以将它们相减得到一个timedelta对象,如清单 2-17 所示。
>>> time1 = datetime.datetime.now()
>>> time2 = datetime.datetime.now()
>>> timediff = time2 - time1
>>> timediff.days
0
>>> timediff.seconds
7
>>> timediff.total_seconds()
7.532031
Listing 2-17.Finding the Difference Between Two Datetimes
如果你想创建一个固定的timedelta,比方说一周,你想添加到一个给定的datetime对象,你可以创建它并使用它,如清单 2-18 所示。
>>> timediff = datetime.timedelta(days=7)
>>> time1 = datetime.datetime.now()
>>> time1.day
10
>>> time2 = time1 + timediff
>>> time2.day
17
Listing 2-18.Creating Timedeltas
2-9.格式化日期和时间
问题
出于显示目的,您需要生成datetime对象的字符串表示。
解决办法
datetime对象有一个字符串格式方法,可以生成一个由规范字符串定义的字符串。
它是如何工作的
方法strftime()获取一个规范字符串,并返回一个字符串,其中替换了相关的值。清单 2-19 中显示了一个基本示例。
>>> time3 = datetime.datetime.now()
>>> time3.strftime("%A %d. %B %Y %I:%M%p")
'Wednesday, 10\. February 2016 09:39AM'
Listing 2-19.Formatting a Datetime String
可能的格式选项如下:
| 管理的 | 意义 | | --- | --- | | `%a` | 作为区域缩写名称的工作日 | | `%A` | 作为区域全名的工作日 | | `%w` | 以 0 到 6 之间的数字表示的星期几 | | `%d` | 以零填充数字表示的月份中的某一天 | | `%b` | 作为区域缩写名称的月份 | | `%B` | 月份作为区域设置全名 | | `%m` | 以零填充数字表示的月份 | | `%y` | 以零填充的两位数表示的年份 | | `%Y` | 以零填充的四位数表示的年份 | | `%H` | 小时(24 小时制)作为零填充数字 | | `%I` | 以零填充数字表示的小时(12 小时制) | | `%p` | 等同于 AM 或 PM 的区域设置 | | `%M` | 以零填充数字表示的分钟 | | `%S` | 秒作为零填充数字 | | `%f` | 微秒作为零填充数字 | | `%z` | UTC 偏移量,形式为+HHMM 或–HHMM | | `%Z` | 时区名称 | | `%j` | 以零填充的三位数表示的一年中的某一天 | | `%U` | 一年中的第一周,将星期日作为一周的第一天 | | `%W` | 一年中的第一周,使用星期一作为一周的第一天 | | `%c` | 日期和时间的区域设置的适当表示 | | `%x` | 区域设置对日期的适当表示 | | `%X` | 区域设置对时间的适当表示 | | `%%` | 文字% |2-10.从字符串中读取日期和时间
问题
您需要获取用户输入并将其转换成一个datetime对象。
解决办法
datetime类可以基于输入字符串实例化一个新对象。
它是如何工作的
如果你分别接受日期和时间的每个元素,你可以在datetime类中直接使用它们,如清单 2-20 所示。
>>> date4 = datetime.datetime(year=1999, month=9, day=21)
>>> date4.weekday()
1
Listing 2-20.Creating a Datetime Object
如果您有一个完整的字符串,使用您自己设计的格式,您可以在strptime()函数中使用它以及一个格式规范字符串。清单 2-21 中给出了一个简单的例子。
>>> date5 = datetime.datetime.strptime("1999-09-21", "%Y-%m-%d")
>>> date5.weekday()
1
Listing 2-21.Using a Format String
格式规范字符串采用与上面的strftime()函数相同的符号。
三、迭代器和生成器
通常,您需要处理来自某个来源的一些数据序列。在 Python 中实现这一点的方法是使用迭代器。标准 Python 中许多可用的数据类型都包含一个可以使用的 iterable 接口。对于那些没有的,您可以创建一个生成器,然后提供一个 iterable 接口。
3-1.遍历列表的内容
问题
你想遍历一个列表的内容。
解决办法
虽然列表是可迭代的,但是您需要使用iter函数来访问相关的迭代器。
它是如何工作的
清单 3-1 展示了如何访问可迭代数据类型的相关迭代器。
>>> my_list = [1, 2, 3, 4]
>>> new_iter = iter(my_list)
>>> new_iter
<list_iterator at 0x1ffb0b8e470>
Listing 3-1.Accessing an Iterator
您可以使用传统的迭代器技术来使用这个新的iterator对象,如清单 3-2 所示。
>>> next(new_iter)
1
>>> next(new_iter)
2
Listing 3-2.Using an Iterator
一旦清空了iterator,就会引发一个异常,如清单 3-3 所示。
>>> next(new_iter)
4
>>> next(new_iter)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-13-ed6082c80a14> in <module>()
----> 1 next(new_iter)
StopIteration:
Listing 3-3.Getting a StopIteration Exception
3-2.提取迭代器的内容
问题
您需要枚举一个迭代器来查看它包含了哪些元素。
解决办法
enumerate内置函数可以接受一个iterable对象,并返回一个包含计数和值的元组列表。
它是如何工作的
enumerate内置函数将一个iterable对象作为输入,返回由一个计数加一个值组成的元组。实际返回的enumerate对象本身是可迭代的,所以你可以像使用其他迭代器一样使用它。清单 3-4 中显示了一个例子。
>>> pets = ['dogs', 'cats', 'lizards', 'pythons']
>>> pet_enum = enumerate(pets)
>>> next(pet_enum)
(0, 'dogs')
>>> next(pet_enum)
(1, 'cats')
Listing 3-4.Iterating Over an Enumerator
默认情况下,计数从 0 开始。您可以通过使用 start 参数来改变这一点,如清单 3-5 所示。
>>> pet_enum2 = enumerate(pets, start=5)
>>> next(pet_enum2)
(5, 'dogs')
Listing 3-5.Enumerating with a Different Count Start
如果您需要立即获得所有枚举值以进行进一步处理,您可以使用清单 3-6 中所示的代码创建一个元组列表。
>>> pet_list = list(enumerate(pets))
>>> pet_list
[(0, 'dogs'), (1, 'cats'), (2, 'lizards'), (3, 'pythons')]
Listing 3-6.Making an Enumerated List
3-3.过滤迭代器
问题
您只需要从迭代器中过滤出选定的项目。
解决办法
内置的filter函数可以选择性地只返回那些对于某些过滤函数为真的元素。
它是如何工作的
内置的filter函数以过滤函数为参数。这个过滤函数应该为您感兴趣的迭代器元素返回 true。在清单 3-7 中,您会看到一个返回从 0 到 9 的奇数的例子。
>>> odd_nums = filter(lambda x: x%2, range(10))
>>> next(odd_nums)
1
>>> next(odd_nums)
3
Listing 3-7.Getting the Odd Numbers Below 10
如您所见,filter返回了一个迭代器,您可以以这种方式使用它。如果你一次需要所有的元素,你可以使用list函数,如清单 3-8 所示。
>>> odd_list = list(filter(lambda x: x%2, range(10)))
>>> odd_list
[1, 3, 5, 7, 9]
Listing 3-8.Getting a List of Odd Numbers
如果要使用负片滤镜,需要使用itertools包。它包括filterfalse函数,该函数返回那些为某些过滤函数返回 false 的元素。清单 3-9 展示了如何使用它来获得所有的偶数。
>>> import itertools
>>> even_list = list(itertools.filterfalse(lambda x: x%2, range(10)))
>>> even_list
[0, 2, 4, 6, 8]
Listing 3-9.Getting a List of Even Numbers
对于这个例子,我们将忽略关于 0 是否是偶数的争论。
3-4.迭代文件的内容
问题
您需要遍历文件的内容进行处理。
解决办法
open函数返回一个文件对象,可以逐行迭代进行处理。
它是如何工作的
从open函数返回的文件对象是一个可迭代对象。迭代内容的通常方式是在一个for循环中,如清单 3-10 所示。
>>> file1 = open('file.csv')
>>> for line in file1:
. . . : print(line)
. . ..:
1,one
2,two
3,three
4,four
Listing 3-10.Looping Over a File
然而,返回的 file 对象实际上是一个 iterable 函数,所以您可以像使用其他迭代器一样使用它。清单 3-11 显示了一个例子。
>>> file1 = open('file.csv')
>>> next(file1)
'1,one\n'
>>> next(file1)
'2,two\n'
Listing 3-11.Iterating Over a File
3-5.对没有迭代器的数据进行迭代
问题
您需要创建一个不可迭代的数据的可迭代版本。
解决办法
Python 的许多内置数据结构已经是可迭代的,因此对生成器的需求较少。但是,当您确实需要一个生成器时,通常没有其他可用的解决方案。
它是如何工作的
本质上,任何将控制权交还给调用它的部分的函数都是生成器。Python 理解您打算在使用yield语句时创建一个生成器。它会自动保存函数在执行yield语句时的状态,这样当next()被调用时你就可以返回到该状态。清单 3-12 显示了一个生成方块序列的简单例子。
def squares(value=0):
while True:
value = value + 1
yield (value-1)*(value-1)
>>> generator = squares()
>>> next(generator)
0
>>> next(generator)
1
>>> next(generator)
4
>>> next(generator)
9
Listing 3-12.Generating the Sequence of Squares
Python 3.3 中的新特性是yield from语句。这是创建生成器函数的一种方式,它使用其他迭代器来生成所需的值。例如,您可以创建一个生成器,它可以计数到某个值,然后再返回,如清单 3-13 所示。
def up_down(value=1):
yield from range(1, value, 1)
yield from range(value, 0, -1)
>>> list(up_down(3))
[1, 2, 3, 2, 1]
Listing 3-13.Generating a Count-Up Count-Down Function
3-6.创建迭代器的标准类
问题
在编程时,有几种情况下您可能有最好作为某种类型的迭代器实现的数据结构。换句话说,您需要创建迭代器的一个标准类。
解决办法
itertools模块提供了大量常用迭代器类别的选择,您可以在许多情况下使用它们。
它是如何工作的
迭代器有几大类,在很多情况下非常有用。例如,清单 3-14 展示了如何制作一个累加器。
>>> import itertools
>>> accumulator = itertools.accumulate(range(10))
>>> next(accumulator)
0
>>> next(accumulator)
1
>>> next(accumulator)
3
>>> next(accumulator)
6
Listing 3-14.Creating an Accumulator
作为一个更复杂的例子,您可以用清单 3-15 中的代码得到两个小于 5 的数字的所有组合。
>>> list(itertools.combinations(range(5), 2))
[(0,1),
(0, 2),
(0, 3),
(0, 4),
(1, 2),
(1, 3),
(1, 4),
(2, 3),
(2, 4),
(3, 4)]
Listing 3-15.Generating Combinations of Pairs
四、文件和输入输出
对于任何类型的数据处理,文件是绝对必要的。很快就会出现信息太多而无法让用户手动输入的情况。您需要能够直接从各种类型的文件中导入信息。
在本章中,你将会看到为了读写文件你需要处理的最常见的任务。您还将看到如何处理其他形式的输入和输出。
4-1.复制文件
问题
您需要将文件从一个位置复制到另一个位置。
解决办法
shutil模块中有两个函数可用于复制文件。
它是如何工作的
复制文件涉及文件的两个部分,所述文件的实际内容和描述该文件的元数据。清单 4-1 展示了如何只复制文件的内容。
>>> import shutil
>>> new_file_path = shutil.copy('file1.txt', 'file2.txt')
Listing 4-1.Copying File Contents
这会将file1.txt复制到file2.txt,将file2.txt的路径返回到变量new_file_path。如果目录名作为第二个参数给出,那么文件将被复制到这个目录中,并使用原始文件名作为新文件名。该功能还将保留文件的权限。如果file1.txt是一个符号链接,这个函数调用将把file2.txt创建为一个单独的文件。如果您想实际创建一个符号链接的副本,您可以使用清单 4-2 中的例子。
>>> shutil.copy('file1.txt', 'file2.txt', follow_symlinks=False)
Listing 4-2.Copying Symlinks
如果想保留更多与文件相关的元数据,可以使用copy2(),如清单 4-3 所示。
>>> shutil.copy2('file1.txt', 'file2.txt')
Listing 4-3.Copying File Contents Plus Metadata
可以复制的元数据量因操作系统和平台而异。它会尽可能多的复制,最坏的情况是只能做和copy()一样多的工作。注意copy2()永远不会返回失败。它也接受follow_symlinks参数,就像copy()一样。
4-2.移动文件
问题
您需要将文件从一个位置移动到另一个位置。这也是重命名文件的方法,将文件从一个文件名移动到另一个文件名。
解决办法
因为重命名和移动文件对文件系统来说本质上是一样的,所以您可以使用shutils.move()或os.rename()来获得相同的结果。
它是如何工作的
使用move功能时,根据源和目的地的位置有不同的行为。如果它们都在同一个文件系统上,则使用函数os.rename()。否则,使用复制功能将文件复制到目标位置,然后删除源文件。默认的复制函数是copy2,但是你可以提交一些其他的函数,如清单 4-4 所示。
>>> import shutil
>>> shutil.move('file1.txt', 'dir2', copy_function=copy)
Listing 4-4.Moving a File
复制函数需要接受一个源和一个目的地,以便被move函数使用。
如果文件在同一个文件系统上,您可以直接使用rename函数,如清单 4-5 所示。
>>> import os
>>> os.rename('file1.txt', 'dir2')
Listing 4-5.Renaming a File
需要注意的主要事项是,如果目标已经存在,并且您有权限写入它,它将被源自动替换。
4-3.读取和写入文本文件
问题
您需要打开、读取和写入文本文件。
解决办法
您可以使用提供的内置open函数打开文件,然后使用read和write方法读取和写入文件。
它是如何工作的
在 Python 中,访问文件是通过文件描述符对象完成的。这是调用内置open函数返回的对象,如清单 4-6 所示。
>>> fd1 = open('file1.txt')
>>> entire_file = fd1.read()
Listing 4-6.Opening a Text File for Reading
文件描述符的read方法将读入文件的全部内容。这通常不是你想做的。为了读入文件的一部分,可以传递一个大小参数,如清单 4-7 所示。
>>> chunk1 = fd1.read(100)
Listing 4-7.Reading the First 100 Bytes of a File
逐行读入数据是如此常见,以至于提供了一种方法来做到这一点。在清单 4-8 中,您可以看到如何在一行中读取,或者如何遍历整个文件。
>>> line1 = fd1.readline()
OR
>>> for line in fd1:
>>> do_my_process(line)
Listing 4-8.Reading a File Line by Line
如果文件不太大,您可以将全部内容读入一个列表,其中每个元素都是文件中的一行。清单 4-9 提供了一个例子。
>>> file_list = fd1.readlines()
>>> first_line = file_list[0]
Listing 4-9.Reading a File into a List
编写一个文本文件只需要一些小的改动。当调用open函数时,默认情况下你将打开只读文件。为了打开文件进行写入,您需要使用不同的模式。感兴趣的模式如下:
4-4.读取和写入 XML 文件
问题
您需要读入并处理 XML 文件,然后写出结果。
解决办法
Python 标准库包括一个 XML 解析器,它可以生成一个元素树,您可以使用它。
它是如何工作的
Python 标准库包括一个ElementTree类,它提供了一种处理 XML 结构化数据的简化方法。首先,您需要用清单 4-10 中的代码打开一个 XML 文件。
>>> import xml.etree.ElementTree as ET
>>> my_tree = ET.parse(‘my_data.xml’)
>>> root = my_tree.getroot()
Listing 4-10.Opening an XML File
一旦有了根元素,就可以查询它并获得标签和属性值,如清单 4-11 所示。
>>> root.tag
'tag value'
>>> root.attrib
{ }
Listing 4-11.Looking at Element Attributes
您可以轻松地遍历任何给定元素的子元素。例如,清单 4-12 展示了如何遍历根元素的所有子元素。
for child in root:
# look at the tag
print(child.tag)
Listing 4-12.Iterating Through the Children of the Root Element
元素也可以作为列表来访问。这意味着您可以使用列表符号,如清单 4-13 所示。
>>> child1 = root[0]
Listing 4-13.Getting the First Child of an Element
使用ElementTree类修改现有的 XML 文件或创建一个新文件也非常容易。可以直接改变元素的文本值,可以使用set()方法设置元素属性。然后,您可以使用write()方法保存更改。清单 4-14 展示了如何创建一个全新的 XML 文件。
>>> a = ET.Element(‘a’)
>>> b = ET.SubElement(a, ‘b’)
>>> c = ET.SubElement(a, ‘c’)
>>> a.write(‘new_file.xml’)
Listing 4-14.Creating a New XML File
4-5.创建目录
问题
您需要创建一个新目录来写出文件。
解决办法
Python 包含了一个名为pathlib的新模块,它提供了一种面向对象的路径处理方式。
它是如何工作的
处理路径的核心类是Path。您需要首先创建一个新的Path对象并设置新的目录名,如清单 4-15 所示。然后可以调用mkdir()方法在文件系统上创建实际的新目录。
>>> from pathlib import Path
>>> p = Path('.')
>>> p.joinpath('subdir1')
>>> p.mkdir()
Listing 4-15.Creating a New Subdirectory
4-6.监视目录的变化
问题
您需要监视一个目录,并在发生变化时进行注册,比如创建了一个新文件。
解决办法
Path类包含了一个检查目录或文件详细属性的方法。
它是如何工作的
要检查当前目录中是否发生了任何更改,您需要用清单 4-16 中的代码获取当前状态。
>>> import pathlib
>>> p = pathlib.Path('.')
>>> modified_time = p.stat().st_mtime
Listing 4-16.Finding the Status of the Current Directory
然后,您可以循环查看这个最后修改时间是否已经更改。如果是这样,那么您知道目录已经更改。如果您需要知道变更是什么,那么您需要创建一个内容列表,并在变更注册前后进行比较。您可以使用glob方法生成当前所有内容的列表,如清单 4-17 所示。
>>> import pathlib
>>> dir_list = sorted(pathlib.Path('.').glob('*'))
Listing 4-17.Getting the Contents of a Directory
4-7.遍历目录中的文件
问题
为了处理一组文件,您需要遍历目录的内容。
解决办法
Path类包含一个迭代内容的函数,给你一个子Path对象的列表。
它是如何工作的
如果想遍历当前目录的内容,可以使用清单 4-18 中的代码。
import pathlib
p = pathlib.Path('.')
for child in p.iterdir():
# Do something with the child object
my_func(child)
Listing 4-18.Iterating Over the Contents of the Current Directory
如果您只想处理文件,您需要包含一个检查,如清单 4-19 所示。
import pathlib
for child in pathlib.Path('.').iterdir():
if child.is_file():
my_func(child)
Listing 4-19.Iterating Over the Files in the Current Directory
4-8.保存数据对象
问题
您需要保存 Python 对象,以便将来在另一个 Python 程序运行中使用。
解决办法
Pickling objects 是序列化 Python 对象以便以后重用的标准方式。
它是如何工作的
Python 标准库包括模块pickle。您需要用常用的open函数打开一个文件,将其传递给 pickle 函数。当您打开文件时,您需要记住包括二进制标志。清单 4-20 展示了如何将一个 Python 对象打包到一个文件中。
>>> import pickle
>>> file1 = open('data1.pickle', 'wb')
>>> pickle.dump(data_obj1, file1)
Listing 4-20.Pickling a Python Object
为了以后重用这些数据,您可以使用清单 4-21 中的代码将其重新加载到 Python 中。
>>> file2 = open('data1.pickle', 'rb')
>>> data_reload = pickle.load(file2)
Listing 4-21.Loading a Pickled Object
完成后,不要忘记关闭文件句柄。
4-9.压缩文件
问题
你需要压缩文件以节省空间。
解决办法
标准库中有许多模块可以帮助您处理 zip、gzip、bzip2、lzma 和 tar 文件。
它是如何工作的
首先,让我们看看如何处理 zip 文件。处理压缩文件的第一步是打开它们。这类似于 Python 中的open函数,如清单 4-22 所示。
>>> import zipfile
>>> my_zip = zipfile.ZipFile('my_file.zip', mode='r')
Listing 4-22.Opening a Zip File
该模式的使用方式与open功能相同。要读取当前存在的 zip 文件,可以使用模式'r'。如果你想创建一个新的 zip 文件,你可以使用模式'w'。您也可以使用模式'a'修改现有的 zip 文件。
您可以使用write方法将文件添加到 zip 文件中,如清单 4-23 所示。
>>> import zipfile
>>> my_zip = zipfile.ZipFile(‘archive1.zip’, mode=’w’)
>>> my_zip.write(‘file1.txt’)
>>> my_zip.close()
Listing 4-23.Adding a File to a Zip Archive
要从现有的 zip 存档中提取文件,请使用清单 4-24 中所示的代码。
>>> import zipfile
>>> my_zip = zipfile.ZipFile(‘archive1.zip’, mode=’r’)
>>> my_zip.extract(‘file1.txt’)
>>> my_zip.close()
Listing 4-24.Extracting One File from a Zip Archive
你可以用方法extractall()提取一切。如果您不知道给定的归档中有哪些文件,您可以使用方法namelist()获得一个列表。
如果您需要直接处理 zip 存档的内容,您可以从存档中读取字节和向存档中写入字节。一旦您打开了 zip 文件,您就可以使用清单 4-25 中的代码来读写归档文件
>>> import zipfile
>>> my_zip = zipfile.ZipFile(‘file1.zip’, ‘a’)
>>> data_in = my_zip.read(‘data1.txt’)
>>> my_zip.write(‘data2.txt’, data_out)
>>> my_zip.close()
Listing 4-25.Reading and Writing Bytes from a Zip Archive
与处理多个文件的压缩存档相反,gzip和bzip2模块都处理单个文件的压缩。要打开任何一种类型,使用清单 4-26 中的样板代码。
>>> import gzip
>>> my_gzip = gzip.GzipFile(‘data1.txt.gz’)
>>> import bz2
>>> my_bzip = bz2.BZ2File(‘data2.txt.bz’)
Listing 4-26.Opening Gzip or Bzip2 Files
在这两种情况下,您都会得到一个实现BufferedIOBase的对象。然后,您可以读写和操作压缩文件中的数据。创建新的 gzip 或 bzip2 对象时,请记住使用适当的模式。
五、Pandas 的 Python 数据分析
Python 真正大的增长领域之一是在科学领域,其中数据分析是一个巨大的组成部分。令人高兴的是,Python 包含了一个用于数据分析的瑞士军队工具,即pandas包,它可以通过 pip 从 PyPi 存储库中安装。pandas提供了很多数据处理和数据处理工具,如果你来自 R 背景,你可能已经习惯了。引入了几种新的数据类型,以及以非常高效的方式处理实际数据的附加功能。它通过构建由numpy包提供的功能来实现这种效率。
5-1.使用 1D 数据
问题
您需要处理一维数据,如数组。
解决办法
pandas 包含了一种新的数据类型,称为Series,用于一维数据。
它是如何工作的
一旦导入了pandas包,就可以使用Series构造函数获取一个已经存在的数据对象,比如一个列表,并将其转换成pandas可以处理的格式。清单 5-1 展示了如何转换一个基本列表。
>>> import pandas as pd
>>> data = [1,2,3,4]
>>> data_pd = pd.Series(data)
Listing 5-1.Converting a List into a Series
您可以选择包含一个索引数组来索引值。在清单 5-1 的例子中,索引只是数据数组中的数字索引。此外,默认情况下,数据留在原处。如果在创建Series对象时需要创建数据的新副本,可以包含参数copy=True. pandas来推断新Series对象使用的数据类型。可以用dtype参数显式设置。可能的值来自numpy。清单 5-2 展示了如何将清单 5-1 中的数据视为浮点。
>>> data_pd.dtype
dtype('int64')
>>> import numpy as np
>>> data_pd2 = pd.Series(data, dtype=np.float64)
>>> data_pd2.dtype
dtype('float64')
Listing 5-2.Explicitly Setting the dtype
所有常用的操作符都被重载,以便与Series对象一起使用,如清单 5-3 所示。
>>> 2 * data_pd
0 2
1 4
2 6
3 8
dtype: int64
Listing 5-3.Basic Arithmetic with Series
访问元素使用与列表相同的语法,如清单 5-4 所示。
>>> data_pd[2]
3
>>> data_pd[3] = 9
Listing 5-4.Accessing Series Data
5-2.使用 2D 数据
问题
你需要处理二维数据。
解决办法
pandas包括一个名为DataFrame的优化对象,支持二维数据集。
它是如何工作的
pandas包含一个名为DataFrame的新对象,它为 2D 数据集创建一个对象。您可以从列表词典或词典列表中创建新的DataFrame。清单 5-5 展示了如何从一个列表字典中创建一个新的DataFrame。
>>> d1 = {'one' : [1,2,3,4], 'two' : [9,8,7,6]}
>>> df1 = pd.DataFrame(d1)
>>> df1
One two
0 1 9
1 2 8
2 3 7
3 4 6
Listing 5-5.Creating a DataFrame
同样,标准算术运算符被重载,以便与DataFrames一起使用。既然有了两个维度,访问元素就有点复杂了。默认情况下,寻址是基于列的。这意味着您可以使用适当的标签直接访问给定的列,如清单 5-6 所示。
>>> df1['one']
0 1
1 2
2 3
3 4
Name: one, dtype: int64
>>> df1['one'][2]
3
Listing 5-6.Accessing DataFrame
Columns
如果想按行访问数据,需要使用DataFrame的iloc或loc属性,如清单 5-7 所示。
>>> df1.loc[1]
one 2
two 8
Name: 1, dtype: int64
>>> df1.loc[1][1]
8
Listing 5-7.Accessing DataFrame Rows
5-3.使用 3D 数据
问题
你需要用pandas处理三维数据集。
解决办法
pandas包含一个名为Panel的新对象,可以有效地处理三维数据集。
它是如何工作的
类似于创建一个DataFrame,创建一个新的Panel对象可以用一个DataFrames的字典来完成。清单 5-8 显示了一个基本的例子。
>>> data_dict = {'item1' : pd.DataFrame(np.random.randn(4, 3)), 'item2' : pd.DataFrame(np.random.randn(4, 2))}
>>> data_panel = pd.Panel(data_dict)
Listing 5-8.Creating a Panel
您可以使用标签访问新Panel中的每个DataFrames。例如,您可以用清单 5-9 中的代码得到item2 DataFrame。
>>> data_panel['item2']
0 1 2
0 -2.126160 -0.677561 NaN
1 -2.110622 -1.535510 NaN
2 -0.387182 -1.412219 NaN
3 -0.370628 0.305436 NaN
Listing 5-9.Accessing DataFrames within a Panel
一旦有了一个DataFrame,就可以像上一节一样访问单个元素。
5-4.从 CSV 文件导入数据
问题
您需要从 CSV(逗号分隔值)文件中导入离线收集的数据。
解决办法
Pandas 包含一个名为read_csv()的方法,可以导入和解析 CSV 文件。
它是如何工作的
在pandas包中有一个顶级方法可以读入 CSV 文件。清单 5-10 显示了最基本的用法。
>>> csv_data = pd.read_csv('data_file.csv')
Listing 5-10.Reading a CSV File
默认情况下,pandas将从 CSV 文件的第一行读取列名。如果在其他行上有列名,可以使用参数header=X将pandas重定向到行X来提取列名。如果您的文件没有列名,您可以使用参数header=None,然后用参数names=[col1, col2, …]提交列名列表。另外,默认情况下,pandas会将第一列视为每一行的标签。如果行标签在另一列中,可以使用参数index_col=X。如果您根本没有任何行标签,您可能想要使用参数index_col=False来强制pandas不使用任何列。
5-5.保存到 CSV 文件
问题
您希望将数据保存到 CSV 文件中,以便与其他人或应用共享。
解决办法
Series和DataFrame对象包含一个名为to_csv()的方法。
它是如何工作的
如果你需要保存你已经处理过的数据,你可以调用带有文件名的to_csv()方法,如清单 5-11 所示。
>>> series_data.to_csv('export_file.csv')
Listing 5-11.Saving to a CSV File
有一些参数可用于更改引用所使用的分隔符或字符。默认情况下,pandas将写出列标题和行标签。如果你只想要数据,你可以使用清单 5-12 中的代码。
>>> data2.to_csv('data_file.csv', header=False, index=False)
Listing 5-12.Saving Data without Headers and Labels
默认情况下,pandas将覆盖已经存在的输出文件。如果您想附加到一个已经存在的文件,您可以用参数mode='a'改变输出模式。
5-6.从电子表格导入
问题
您希望从电子表格中导入现有数据。
解决办法
pandas包括一个从电子表格文件导入单个工作表的方法,以及一个包装类,如果你需要从一个给定的文件中处理多个工作表的话。
它是如何工作的
如果你只需要从一个文件中导入一个工作表,你可以使用清单 5-13 中的代码来完成。
>>> data_frame1 = pd.read_excel('data_file.xsl', sheetname='Sheet1')
Listing 5-13.Importing a Single Spreadsheet Sheet
这将把数据作为单个DataFrame对象导入。
如果您希望处理多个工作表,为了方便访问,将文件一次性加载到包装类中会更有效。幸运的是,pandas有这样一个包装类,如清单 5-14 所示。
>>> excel_data = pd.ExcelFile('data_file.xsl')
Listing 5-14.Wrapping a Spreadsheet
in pandas
然后,您可以将这个对象传递给read_excel()方法,而不是一个文件名。
5-7.保存到电子表格
问题
您想要将您的DataFrame保存到电子表格文件中。
解决办法
DataFrame类包含一个名为to_excel()的方法,它将数据写出到一个文件中。
它是如何工作的
编写输出的最简单方法如清单 5-15 所示。
>>> df.to_excel('output_file.xsl', sheet='Sheet1')
Listing 5-15.Writing Output to a Spreadsheet File
pandas会根据文件扩展名选择不同的编写引擎。您也可以使用文件名结尾.xslx保存文件。
5-8.得到头部和尾部
问题
您希望查询数据以了解其结构。
解决办法
有一些函数可以用来查看给定数据集的开头或结尾。一旦你开始使用大型数据集,它们就非常有用。
它是如何工作的
Series和DataFrame对象都有名为head()和tail()的方法。默认情况下,对于给定的数据集,它们会分别给出前五个条目或后五个条目。如果您想查看更多或更少的数据,您可以包含一个参数来表示您想要查看的条目数量,如清单 5-16 所示。
>>> data_series = pd.Series(np.random.randn(1000))
>>> data_series.head(2)
0 -0.399614
1 1.307006
dtype: float64
>>> data_series.tail(2)
998 0.866424
999 -0.321825
dtype: float64
Listing 5-16.Getting the First and Last Two Data Entries
5-9.汇总数据
问题
您希望获得数据集的统计摘要。
解决办法
pandas中引入的新数据对象包括一组用于提供数据汇总统计的方法。
它是如何工作的
有几种方法可用于个体统计,如均值或标准差。还有一个名为describe()的方法提供完整的摘要,如清单 5-17 所示。
>>> data_series.describe()
count 1000.000000
mean -0.029861
std 0.990916
min -3.261506
25% -0.697940
50% -0.048408
75% 0.646266
max 3.595167
dtype: float64
Listing 5-17.Describing Your Data
这些值中的每一个都可以通过单独的方法获得。例如,清单 5-18 展示了如何验证标准差等于方差的平方根。
>>> data_series.std() ** 2
0.9819137628988116
>>> data_series.var()
0.9819137628988115
Listing 5-18.Comparing the Standard Deviation and the Variance
这看起来不错,在浮点数的精度范围内。
5-10.分类数据
问题
您希望对数据进行一些排序作为预处理。
解决办法
Series和DataFrame对象包含按索引或按值排序的方法。
它是如何工作的
如果您的数据是以随机顺序输入的,您可能需要在进行实际分析之前做一些预处理。清单 5-19 展示了如何根据行或列标签对DataFrame进行排序。
>>> df = pd.DataFrame({'one' : [1,2,3], 'two' : [4,5,6], 'three' : [7,8,9]}, index=['b','c','a'])
>>> df
one three two
b 1 7 4
c 2 8 5
a 3 9 6
>>> df.sort_index()
one three two
a 3 9 6
b 1 7 4
c 2 8 5
>>> df.sort_index(axis=1, ascending=False)
two three one
b 4 7 1
c 5 8 2
a 6 9 3
Listing 5-19.Sorting a DataFrame by Index
您可能希望对数据进行排序的另一种方式是根据实际数据值。在这种情况下,您需要决定根据哪一列进行排序。清单 5-20 展示了如何按降序对第二列进行排序。
>>> df.sort_values(by='two', ascending=False)
one three two
a 3 9 6
c 2 8 5
b 1 7 4
Listing 5-20.Sorting a DataFrame by Values
Note
从版本 0.17.0 开始,这些方法返回一个新的排序对象,除非您使用参数inplace=True。在这个版本之前,排序发生在原始数据对象中。
5-11.按行或按列应用函数
问题
您需要对整个行或列执行函数。
解决办法
DataFrames和Panels都有一个叫做apply()的方法,可以用来跨列或行应用函数。
它是如何工作的
清单 5-21 展示了如何找到每一列和每一行的平均值。
>>> df = pd.DataFrame({'one' : [1,2,3], 'two' : [4,5,6], 'three' : [7,8,9]}, index=['b','c','a'])
>>> df.apply(np.mean)
one 2.0
three 8.0
two 5.0
dtype: float64
>>> df.apply(np.mean, axis=1)
b 4.0
c 5.0
a 6.0
dtype: float64
Listing 5-21.Averaging Rows and Columns
这是使用 lambda 函数的好地方,如果你的函数足够简单,可以放入一个表达式中。清单 5-22 显示了一个简单的 lambda 函数的例子,该函数简单地将数据帧中的值加倍。
>>> df.apply(lambda x: 2*x, axis=1)
one three two
b 2 14 8
c 4 16 10
a 6 18 12
[3 rows x 3 columns]
Listing 5-22.Applying a Lambda Function on a Data Set
5-12.按元素应用函数
问题
您需要将函数应用于数据集中的所有元素或元素的某个子集。
解决办法
新的 pandas 数据对象有两个名为map()和applymap()的有用方法,可用于将函数应用到单个元素组。
它是如何工作的
有时候,您需要对数据集的单个元素应用一些函数。清单 5-23 展示了如何对一个数据集的所有元素求平方。
>>> df = pd.DataFrame({'one' : [1,2,3], 'two' : [4,5,6], 'three' : [7,8,9]}, index=['b','c','a'])
>>> df.applymap(lambda x: x*x)
one three two
b 1 49 16
c 4 64 25
a 9 81 36
Listing 5-23.Squaring Data Elements
如果你只想对一个给定的列应用一个函数,清单 5-24 展示了如何将列2中的值加倍。
>>> df['two'].map(lambda x: 2*x)
b 8
c 10
a 12
Name: two, dtype: int64
Listing 5-24.Doubling a Single Column of Elements
5-13.迭代数据
问题
作为处理工作流的一部分,您需要迭代您的数据集。
解决办法
数据对象是可迭代的对象,在大多数情况下,当你需要循环所有的元素时,可以使用它。
它是如何工作的
如果您想对pandas提供的一个新数据对象进行基本迭代,您需要意识到它们的行为略有不同。迭代的基本类型有
Series:迭代每个元素DataFrame:遍历各列Panel:迭代项目标签
例如,清单 5-25 展示了如何使用迭代计算每一列的平均值。
>>> df = pd.DataFrame({'one' : [1,2,3], 'two' : [4,5,6], 'three' : [7,8,9]}, index=['b','c','a'])
>>> for col in df:
....: print(df[col].mean())
....:
2.0
8.0
5.0
Listing 5-25.Averaging Each Column of a DataFrame
如果您需要迭代一个DataFrame的行,清单 5-26 显示了一个例子。
>>> for row_index,row in df.iterrows():
....: print(row_index)
....: print(row)
....:
b
one 1
three 7
two 4
Name: b, dtype: int64
c
one 2
three 8
two 5
Name: c, dtype: int64
a
one 3
three 9
two 6
Name: a, dtype: int64
Listing 5-26.Iterating Over Rows
然而,请注意,迭代可能会相当慢。只要有可能,您可能会希望找到另一种方式来表达您需要执行的处理工作流。