pandas Series 和 DataFrame
Series
Series是一个增强的一维数组。数组仅使用从零开始的整数索引,而Series支持自定义索引,甚至包括字符串等非整数索引。Series还提供额外的功能,使它们能更方便地完成许多面向数据科学的任务。例如,考虑到Series中可能有缺失数据,默认情况下,许多Series操作都会忽略缺失数据。
使用默认索引创建Series
默认情况下,一个Series的整数索引是从0开始顺序编号的。
下面从一个整数列表中创建一个学生成绩的Series:
In [1]: import pandas as pd
In [2]: grades = pd.Series([87,100,94])
初始化变量也可以是元组、字典、数组、另一个Series或单个值。
Series 的显示
pandas以两列格式显示Series,其索引在左列中左对齐,而值在右列中右对齐。在列出Series元素之后,pandas显示了底层array元素的数据类型(dtype):
In [10]: grades
Out[10]:
0 87
1 100
2 94
dtype: int64
可以看到,与以相同的两列格式显示列表的对应代码相比,以这种格式显示Series非常容易。
创建一个内部元素值相同的Series
可以创建具有相同元素值的Series:
In [12]: pd.Series(98.6,range(3))
Out[12]:
0 98.6
1 98.6
2 98.6
dtype: float64
第二个参数是一个一维的可迭代对象(例如列表、数组或范围),它包含了Series的索引。索引的数量决定了元素的数量。
访问Series中的元素
可以通过包含索引的方括号来访问Series的元素:
In [11]: grades[0]
Out[11]: 87
为Series生成描述性统计信息
Series提供了许多用于常见任务(包括生成各种描述性统计信息)的方法。以下列出了count(元素个数)、mean(均值)、min(最小值)、max(最大值)和std(标准偏差)方法:
In [3]: grades.count()
Out[3]: 3
In [4]: grades.mean()
Out[4]: 93.66666666666667
In [5]: grades.min()
Out[5]: 87
In [6]: grades.max()
Out[6]: 100
In [7]: grades.std()
Out[7]: 6.506407098647712
这些都是函数式约简。调Series的describe方法会给出所有的统计信息以及更多信息:
In [9]: grades.describe()
Out[9]:
count 3.000000
mean 93.666667
std 6.506407
min 87.000000
25% 90.500000
50% 94.000000
75% 97.000000
max 100.000000
dtype: float64
其中,25%、50%和75%是四分位数:
- 50%表示排序值的中值。
- 25%代表排序后前一半值的中值。
- 75%代表排序后后一半值的中值。
对于四分位数,如果有两个中间元素,那么它们的平均值就是该四分位数的中值。而我们Series中只有三个值,因此25%的四分位数是87和94的平均值,而75%的四分位数是94和100的平均值。四分位数****间距是75%的四分位数减去25%的四分位数,这是像标准偏差和方差的另一种度量离散度的方法。当然,四分位数和四分位数间距在较大的数据集中会更有用。
创建带自定义索引的Series
可以使用关键字参数index来指定自定义索引:
In [14]: grades = pd.Series([87,100,94],index=['Wally','Eva','Sam'])
In [15]: grades
Out[15]:
Wally 87
Eva 100
Sam 94
dtype: int64
在本例中,我们使用了字符串作为索引,但是也可以使用其他不可变类型,包括不是从0开始的整数和非连续整数。同样,请再次注意pandas如何精确地格式化一个Series以供显示。
字典作为初始化值
如果使用字典初始化Series,则它的键将成为Series的索引,它的值会被设定为Series的元素值:
In [16]: grades = pd.Series({'Wally':87,'Eva':100,'Sam':94})
In [17]: grades
Out[17]:
Wally 87
Eva 100
Sam 94
dtype: int64
使用自定义索引访问Series中的元素
在使用自定义索引的Series中,可以通过包含自定义索引值的方括号来访问单个元素:
In [19]: grades['Eva']
Out[19]: 100
如果自定义索引是可以表示有效Python标识符的字符串,则pandas会自动将它们添加到Series中,作为可以通过点(.)访问的属性:
In [20]: grades.Wally
Out[20]: 87
Series还具有内置属性。例如,dtype属性返回底层array的元素类型:
In [24]: grades.dtypes
Out[24]: dtype('int64')
values属性返回底层array:
In [23]: grades.values
Out[23]: array([ 87, 100, 94], dtype=int64)
创建一个字符串Series
如果一个Series包含字符串,那么可以使用它的str属性来调用元素上的字符串方法。首先,让我们创建一个硬件相关的字符串Series:
In [26]: hardware = pd.Series(['Hammer','Saw','Wrench'])
In [27]: hardware
Out[27]:
0 Hammer
1 Saw
2 Wrench
dtype: object
请注意,pandas还会将字符串元素值右对齐,并且字符串的dtype是object。
判断Series中是否包含某字符
调用每个元素上的字符串方法contains来确定每个元素的值是否包含’a’
In [28]: hardware.str.contains('a')
Out[28]:
0 True
1 True
2 False
dtype: bool
pandas返回了一个包含布尔值的新Series,该值指示了contains方法对每个元素的执行结果—索引2(‘Wrench’)处的元素不包含’a’,因此其在所得Series中的对应位置的元素为False。请注意,pandas在内部处理了迭代—这是函数式编程的另一个示例。str属性提供了许多类似于Python字符串类型的字符串处理方法。有关方法列表,参见:this link
以下使用字符串方法upper生成一个新的Series,其中包含hardware变量中每个元素的大写版本:
In [29]: hardware.str.upper()
Out[29]:
0 HAMMER
1 SAW
2 WRENCH
dtype: object
DataFrame
DataFrame是增强的二维数组。与Series类似,DataFrame可以具有自定义的行和列索引,并提供额外的操作和功能,使其能更方便地执行许多面向数据科学的任务。DataFrame还支持缺失数据情况下的处理。DataFrame中的每一列都是一个Series。代表每个列的Series可能包含不同的元素类型,在我们讨论将数据集加载到DataFrame时,会看到这一点。
从字典中创建一个DataFrame
从字典中创建一个DataFrame让我们从字典中创建一个DataFrame,它表示学生在三次考试中的成绩:
In [32]: grades_dict = {'Wally':[87,96,70],'Eva':[100,87,90],'Sam':[94,77,90],'Katie':[100,81,82],'Bob':[83,65,85]}
In [33]: grades = pd.DataFrame(grades_dict)
In [34]: grades
Out[34]:
Wally Eva Sam Katie Bob
0 87 100 94 100 83
1 96 87 77 81 65
2 70 90 90 82 85
pandas以表格格式显示DataFrame,其中索引在索引列中左对齐,其余列的值右对齐。字典的键变成列名,而与每个键相关联的值成为对应列中的元素值。稍后,我们将展示如何“翻转”行和列。默认情况下,行索引是从0开始自动生成的整数。
使用index属性自定义DataFrame的索引
在创建DataFrame时,可以使用index关键字参数指定自定义索引,如下所示:
pd.DataFrame(grades_dict,index=['Test1','Test2','Test3'])
使用index属性将DataFrame的索引从连续整数更改为标签:
In [35]: grades.index = ['Test1','Test2','Test3']
In [36]: grades
Out[36]:
Wally Eva Sam Katie Bob
Test1 87 100 94 100 83
Test2 96 87 77 81 65
Test3 70 90 90 82 85
在指定索引时,必须提供元素数等于DataFrame中的行数的一维合集;否则,将引发ValueError。Series还提供了一个index属性,用于更改现有Series的索引。
访问DataFrame元素的列
使用pandas的一个好处是可以通过多种不同的方式方便快速地查看数据,包括选择部分数据。让我们先按名称获取Eva的成绩,这会将她所在的列显示为一个Series:
In [37]: grades['Eva']
Out[37]:
Test1 100
Test2 87
Test3 90
Name: Eva, dtype: int64
如果DataFrame的列名字符串是有效的Python标识符,则可以将它们用作属性。让我们用Sam属性来得到Sam的成绩:
In [38]: grades.Sam
Out[38]:
Test1 94
Test2 77
Test3 90
Name: Sam, dtype: int64
通过loc和iloc属性选择行
尽管DataFrame支持使用[]
建立索引的功能,但pandas的文档建议使用属性loc、iloc、at和iat来进行行操作。这些属性已被优化为专门用于访问DataFrame,还能提供除了[]
之外的其他功能。此外,文档还指出,使用[]建立索引通常会产生数据副本,如果试图通过赋值给[]
操作的结果来为DataFrames设定新值,则会导致一个逻辑错误。
可以使用DataFrame的loc属性通过行标签访问行。下面列出了’Test1’行中的所有成绩:
In [40]: grades.loc['Test1']
Out[40]:
Wally 87
Eva 100
Sam 94
Katie 100
Bob 83
Name: Test1, dtype: int64
还可以使用iloc属性通过基于零的整数索引访问行(iloc中的i表示它与整数索引一起使用)。下面列出了第二行中的所有成绩:
In [41]: grades.iloc[1]
Out[41]:
Wally 96
Eva 87
Sam 77
Katie 81
Bob 65
Name: Test2, dtype: int64
通过带有loc和iloc属性的切片和列表选择行
索引可以是一个切片。当使用包含带有loc的标签的切片时,指定的范围包括切片右端的索引(‘Test3’):
In [43]: grades.loc['Test1':'Test3']
Out[43]:
Wally Eva Sam Katie Bob
Test1 87 100 94 100 83
Test2 96 87 77 81 65
Test3 70 90 90 82 85
当将iloc属性与包含整数索引的切片一起使用时,指定的范围不包括切片右端的索引(2):
In [44]: grades.iloc[0:2]
Out[44]:
Wally Eva Sam Katie Bob
Test1 87 100 94 100 83
Test2 96 87 77 81 65
要选择特定的行,请在loc或iloc属性中使用列表代替切片:
In [45]: grades.loc[['Test1','Test3']]
Out[45]:
Wally Eva Sam Katie Bob
Test1 87 100 94 100 83
Test3 70 90 90 82 85
In [46]: grades.iloc[[0,2]]
Out[46]:
Wally Eva Sam Katie Bob
Test1 87 100 94 100 83
Test3 70 90 90 82 85
选择行与列的子集
到目前为止,我们只选择了整行。可以使用两个切片、两个列表或切片和列表的组合来选择行与列,从而专注于DataFrame的一个小子集。
假设只想查看Test1和Test2中Eva和Katie的成绩,可以通过使用带有两个连续行的切片的loc和两个非连续列的列表来实现:
In [48]: grades.loc['Test1':'Test2',['Eva','Katie']]
Out[48]:
Eva Katie
Test1 100 100
Test2 87 81
切片’Test1’:'Test2’选择索引为Test1和Test2的这两行。列表[‘Eva’,‘Katie’]表示仅从这两列中选择相应的成绩。
让我们使用带有列表和切片的iloc来选择第一个和第三个测试成绩,并获取这些测试成绩的前三列:
In [49]: grades.iloc[[0,2],0:3]
Out[49]:
Wally Eva Sam
Test1 87 100 94
Test3 70 90 90
布尔索引
pandas更强大的选择功能之一是布尔索引。例如,让我们选择所有的A等成绩,即大于或等于90分的成绩:
In [50]: grades[grades >= 90]
Out[50]:
Wally Eva Sam Katie Bob
Test1 NaN 100.0 94.0 100.0 NaN
Test2 96.0 NaN NaN NaN NaN
Test3 NaN 90.0 90.0 NaN NaN
pandas检查每个成绩,以确定其值是否大于或等于90,如果是,则令其包含在新的DataFrame中。条件为False的成绩在新的DataFrame中表示为NaN(不是数字)。NaN是pandas用来表示缺失值的符号。
让我们选择出分数为80~89的所有的B等成绩:
In [51]: grades[(grades >= 80) & (grades < 90)]
Out[51]:
Wally Eva Sam Katie Bob
Test1 87.0 NaN NaN NaN 83.0
Test2 NaN 87.0 NaN 81.0 NaN
Test3 NaN NaN NaN 82.0 85.0
pandas的布尔索引使用Python位运算符&(按位与)而不是布尔运算符and来组合多个条件。对于or条件,使用|(按位或)。NumPy还支持arrays的布尔索引,但始终返回仅包含满足条件的值的一维数组。
通过行和列访问特定的DataFrame单元格
可以使用DataFrame的at和iat属性从DataFrame获取单个值。像loc和iloc一样,at使用标签,而iat使用整数索引。在每种情况下,行和列的索引必须用逗号分隔。让我们选择出Eva的Test2成绩(87)和Wally的Test3成绩(70):
In [52]: grades.at['Test2','Eva']
Out[52]: 87
In [53]: grades.iat[2,0]
Out[53]: 70
还可以为特定的元素分配新值。让我们使用at将Eva的Test2成绩更改为100,然后使用iat将其更改回87:
In [54]: grades.at['Test2','Eva']=100
In [55]: grades.at['Test2','Eva']
Out[55]: 100
In [56]: grades.iat[1,2] = 87
In [57]: grades.iat[1,2]
Out[57]: 87
描述性统计
Series和DataFrame都有一个describe方法,该方法计算数据的基本描述性统计并将结果以DataFrame的形式返回。在DataFrame中,统计数据是按列计算的(我们很快将看到如何翻转行和列):
In [58]: grades.describe()
Out[58]:
Wally Eva Sam Katie Bob
count 3.000000 3.000000 3.000000 3.000000 3.000000
mean 84.333333 96.666667 90.333333 87.666667 77.666667
std 13.203535 5.773503 3.511885 10.692677 11.015141
min 70.000000 90.000000 87.000000 81.000000 65.000000
25% 78.500000 95.000000 88.500000 81.500000 74.000000
50% 87.000000 100.000000 90.000000 82.000000 83.000000
75% 91.500000 100.000000 92.000000 91.000000 84.000000
max 96.000000 100.000000 94.000000 100.000000 85.000000
默认情况下,pandas使用浮点值计算描述性统计信息,并以六位数字的精度显示它们。可以使用pandas的set_option函数控制精度和其他默认设置:
In [59]: pd.set_option('precision',2)
In [60]: grades.describe()
Out[60]:
Wally Eva Sam Katie Bob
count 3.00 3.00 3.00 3.00 3.00
mean 84.33 96.67 90.33 87.67 77.67
std 13.20 5.77 3.51 10.69 11.02
min 70.00 90.00 87.00 81.00 65.00
25% 78.50 95.00 88.50 81.50 74.00
50% 87.00 100.00 90.00 82.00 83.00
75% 91.50 100.00 92.00 91.00 84.00
max 96.00 100.00 94.00 100.00 85.00
对于学生成绩,这些统计数据中最重要的可能是平均值。只需在DataFrame上调用mean方法即可为每名学生计算该值:
In [61]: grades.mean()
Out[61]:
Wally 84.33
Eva 96.67
Sam 90.33
Katie 87.67
Bob 77.67
dtype: float64
接下来,我们将展示如何通过一行附加代码来获得每个测试中所有学生的平均成绩。
使用T属性转置DataFrame
可以使用T属性快速地转置行和列—这样行就变成了列,列就变成了行:
In [62]: grades.T
Out[62]:
Test1 Test2 Test3
Wally 87 96 70
Eva 100 100 90
Sam 94 87 90
Katie 100 81 82
Bob 83 65 85
T属性返回DataFrame的转置视图(不是副本)。
假设不是按学生获取摘要统计信息,而是针对测试来进行分析。对此,我们只需在grades.T上调用describe,如下所示:
In [63]: grades.T.describe()
Out[63]:
Test1 Test2 Test3
count 5.00 5.00 5.00
mean 92.80 85.80 83.40
std 7.66 13.81 8.23
min 83.00 65.00 70.00
25% 87.00 81.00 82.00
50% 94.00 87.00 85.00
75% 100.00 96.00 90.00
max 100.00 100.00 90.00
要查看每个测试中所有学生的平均成绩,只需在T属性上调用mean:
In [64]: grades.T.mean()
Out[64]:
Test1 92.8
Test2 85.8
Test3 83.4
dtype: float64
按行索引排序
通常会对数据进行排序,以提高可读性。可以根据DataFrame的索引或值对其行或列进行排序。让我们使用sort_index及其关键字参数ascending=False(默认情况下是按升序排序)对各行按行索引降序排序。这将返回一个包含已排序数据的新DataFrame:
In [65]: grades.sort_index(ascending=False)
Out[65]:
Wally Eva Sam Katie Bob
Test3 70 90 90 82 85
Test2 96 100 87 81 65
Test1 87 100 94 100 83
按列索引排序
现在根据列名按升序(从左到右)排序。传递axis=1关键字参数表示我们希望依据列索引进行排序,而不是行索引—axis=0(默认值)表示按行索引排序:
In [67]: grades.sort_index(axis=1)
Out[67]:
Bob Eva Katie Sam Wally
Test1 83 100 100 94 87
Test2 65 100 81 87 96
Test3 85 90 82 90 70
按列元素值排序
假定想按降序查看Test1的成绩,以便可以按分数从高到低的顺序查看学生的姓名。对此,可以按以下方式调用sort_values方法:
In [66]: grades.sort_values(by='Test1',axis=1,ascending=False)
Out[66]:
Eva Katie Sam Wally Bob
Test1 100 100 94 87 83
Test2 100 81 87 96 65
Test3 90 82 90 70 85
by和axis关键字参数共同决定哪些值将被排序。在本例中,我们根据Test1的列值(axis=1)进行排序。
当然,如果成绩和名称位于一列中,则可能更易于阅读,因此我们可以对转置的DataFrame进行排序。在这里,我们不需要指定axis关键字参数,因为默认情况下sort_values对指定列中的数据进行排序:
In [68]: grades.T.sort_values(by='Test1',ascending=False)
Out[68]:
Test1 Test2 Test3
Eva 100 100 90
Katie 100 81 82
Sam 94 87 90
Wally 87 96 70
Bob 83 65 85
最后,由于只需要对Test1的成绩排序,所以可能根本不想看到其他测试。为此,让我们把选择和排序结合起来:
In [69]: grades.loc['Test1'].sort_values(ascending=False)
Out[69]:
Eva 100
Katie 100
Sam 94
Wally 87
Bob 83
Name: Test1, dtype: int64