ch05 pandas入门


pandas是本书后续内容的⾸选库。它含有使数据清洗和分析⼯作变得更快更简单的数据结构和操作⼯具。pandas经常和其 ⼯具⼀同使⽤,如数值计算⼯具NumPy和SciPy,分析库 statsmodels和scikit-learn,和数据可视化库matplotlib。pandas是基于NumPy数组构建的,特别是基于数组的函数和不使⽤for 循环的数据处理。
虽然pandas采⽤了⼤量的NumPy编码⻛格,但⼆者最⼤的不同 是pandas是专⻔为处理表格和混杂数据设计的。⽽NumPy更适 合处理统⼀的数值数组数据。

一、pandas数据结构

1.1 Series

Series是⼀种类似于⼀维数组的对象,它由⼀组数据(各种NumPy数据类型)以及⼀组与之相关的数据标签(即索引)组成

import pandas as pd

# 创建Series
obj = pd.Series([4, 7, -5, 3])
obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])

# 基本属性
obj.values
obj.index 

# 基本索引
obj2['a']
obj2['d'] = 6
obj2[['c', 'a', 'd']]

# 基本运算(可以使用numpy运算)
obj2[obj2 > 0]
obj2 * 2
np.exp(obj2)

# 使用字典的方法
'b' in obj2
'e' in obj2

# 由字典生成Series
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
obj3 = pd.Series(sdata)

# 由指定索引生成Series
states = ['California', 'Ohio', 'Oregon', 'Texas']
obj4 = pd.Series(sdata, index=states)

# 检验缺失数据
pd.isnull(obj4)
pd.notnull(obj4)
obj4.isnull()
obj4.notnull()

# Series的name属性
obj4.name = 'population'
obj4.index.name = 'state'

# 以赋值的方式就地修改索引
obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']

1.2 DataFrame

DataFrame是⼀个表格型的数据结构,它含有⼀组有序的列,每 列可以是不同的值类型(数值、字符串、布尔值等)。
DataFrame既有⾏索引也有列索引,它可以被看做由Series组成的字典(共⽤同⼀个索引)。DataFrame中的数据是以⼀个或多个⼆维块存放的(⽽不是列表、字典或别的⼀维数据结构)。有 关DataFrame内部的技术细节远远超出了本书所讨论的范围。

虽然DataFrame是以⼆维结构保存数据的,但你仍然可以轻松地将其表示为更⾼维度的数据(层次化索引的表格型结构,这是pandas中许多⾼级数据处理功能的关键要素,我们会在第8章讨论这个问题)

# 创建DataFrame
   #方法一:传入由等长列表或numpy数组组成的字典
data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002, 2003],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
frame = pd.DataFrame(data)
   #方法二:嵌套字典
pop = {'Nevada': {2001: 2.4, 2002: 2.9},
       'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop)

# 获得行列索引
frame2.index
frame2.columns

# 截取前几行数据
frame.head()
frame.head(10)

# 指定列的顺序
pd.DataFrame(data, columns=['year', 'state', 'pop'])

# 获得某一列数据
frame2['state']  #索引
frame2.year  #属性

# 获得某一行数据
frame2.loc['three']  #loc属性

# 给某一列赋值
frame2['debt'] = 16.5
frame2['debt'] = np.arange(6.)

# 自动对齐功能
val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val

# 添加新的列
frame2['eastern'] = frame2.state == 'Ohio'

# 删除列
del frame2['eastern']

# 转置
frame3.T

# 在用嵌套字典创建DataFrame时,内层字典的键会被合并、排序以形成最终的索引。
# 如果明确指定了索引,则不会这样:
pd.DataFrame(pop, index=[2001, 2002, 2003])

# 设置行列名称
frame3.index.name = 'year'
frame3.columns.name = 'state'

# values属性(将DataFrame数据以二维ndarray形式返回数据)
frame3.values

注意:通过索引⽅式返回的列只是相应数据的视图⽽已,并不是副本。因此,对返回的Series所做的任何就地修改全都 会反映到源DataFrame上。通过Series的copy⽅法即可指定复制列

~ 可输入给DataFrame构造器的函数
在这里插入图片描述

二、索引

2.1 索引对象

pandas的索引对象负责管理轴标签和其他元数据(⽐如轴名称等)。构建Series或DataFrame时,所⽤到的任何数组或其他序列的标签都会被转换成⼀个Index:

obj = pd.Series(range(3), index=['a', 'b', 'c'])
index = obj.index
index
index[1:]

# index是不可变的,不能通过直接给其赋值来变更
index[1] = 'd'  #TypeError

# 通过pd.index()创建的索引对象与pd.Series()指定的索引性质是相同的
labels = pd.Index(np.arange(3))
obj2 = pd.Series([1.5, -2.5, 0], index=labels)
obj2.index is labels
	#注意:虽然⽤户不需要经常使⽤Index的功能,但是因为⼀些操作会⽣成包含被索引化的数据,
	#理解它们的⼯作原理是很重要的。

# 除了类似于数组,index的功能也类似于一个固定大小的集合
frame3
frame3.columns
'Ohio' in frame3.columns
2003 in frame3.index
# 不过与python的集合相比,index可以包含重复标签
dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar'])
dup_labels

~ 索引的常用方法和属性
在这里插入图片描述

2.2 重新索引

pandas对象的⼀个重要⽅法是reindex,其作⽤是创建⼀个新对象,它的数据符合新的索引。
~Series

# rindex方法
obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
obj2
  
#对于时间序列这样的有序数据,重新索引时可能需要做⼀些插值处理。
#method选项即可达到此⽬的,例如,使⽤ffill可以实现前向值填充:
obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
obj3.reindex(range(6), method='ffill')

~DataFrame
借助DataFrame,reindex可以修改(⾏)索引和列。

# 只传递⼀个序列时,会重新索引结果的⾏
frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
                     index=['a', 'c', 'd'],
                     columns=['Ohio', 'Texas', 'California'])
frame
frame2 = frame.reindex(['a', 'b', 'c', 'd'])
frame2  #增加了原本不存在的行索引,会给该行数据填充NaN

# 列可以⽤columns关键字重新索引
states = ['Texas', 'Utah', 'California']
frame.reindex(columns=states)

~reindex函数的参数
在这里插入图片描述

2.3 索引、选取和过滤

2.3.1 基本方法

Series索引(obj[…])的⼯作⽅式类似于NumPy数组的索引,只不过Series的索引值不只是整数。
~Series

# Series的索引值不只是整数
obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
obj
obj['b']
obj[1]
obj[2:4]
obj[['b', 'a', 'd']]
obj[[1, 3]]
obj[obj < 2]

# 利⽤标签的切⽚运算与普通的Python切⽚运算不同,其末端是包含的
obj['b':'c']

# ⽤切⽚可以对Series的相应部分进⾏设置
obj['b':'c'] = 5

~DataFrame

# ⽤⼀个值或序列对DataFrame进⾏索引其实就是获取⼀个或多个列
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
                    index=['Ohio', 'Colorado', 'Utah', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
data
data['two']
data[['three', 'one']]

# 几种特殊情况:
	# 通过切片或布尔型数组选取数据
data[:2]
data[data['three'] > 5]
	# 通过布尔型DataFrame(⽐如下⾯这个由标量⽐较运算得出的)进⾏索引
data < 5
data[data < 5] = 0
data

2.3.2 用loc和iloc进行选取

对于DataFrame的⾏的标签索引,我引⼊了特殊的标签运算符loc和iloc。它们可以让你⽤类似NumPy的标记,使⽤轴标签(loc)或整数索引(iloc),从DataFrame选择⾏和列的⼦集。

data = pd.DataFrame(np.arange(16).reshape((4, 4)),
                    index=['Ohio', 'Colorado', 'Utah', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
data

# loc用法:适用于使用标签的索引
data.loc['Colorado', ['two', 'three']]
data.loc[:'Utah', 'two']

# iloc用法:整数索引
	#iloc的各个参数是各个行索引和列索引的位置。
	#例如,本例中行索引Utah的位置为2.
data.iloc[2, [3, 0, 1]]
data.iloc[2]
data.iloc[[1, 2], [3, 0, 1]]
data.iloc[:, :3][data.three > 5]

~DataFrame的索引选项
在这里插入图片描述

2.4 整数索引

处理整数索引的pandas对象与python内置的列表和元组的索引语法不同,例如下列代码会出错:

ser = pd.Series(np.arange(3.), index=range(3,6))
ser
ser[-1]
#在pandas中,如果轴索引含有整数,数据选取总会使用标签。
#而这里的-1并不是轴索引的标签。

对于⾮整数索引,不会产⽣歧义:

ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
ser2[-1]

为了更准确,请使用loc(标签)或iloc(整数):

ser[:1]
ser.loc[:1]
ser.iloc[:1]

2.5 带有重复标签的轴索引

虽然许多pandas函数(如reindex)都要求标签唯⼀,但这并不是强制性的。我们来看看下⾯这个简单的带有重复索引值的Series:

obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
obj

# 索引的is_unique属性可以告诉你它的值是否是唯⼀的:
obj.index.is_unique

# 如果某个索引对应多个值,则返回⼀个Series;
# ⽽对应单个值的,则返回⼀个标量值:
obj['a']
obj['c']

# 对DataFrame的⾏进⾏索引时也是如此:
df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
df
df.loc['b']

三、基本功能

3.1 丢弃指定轴上的项

丢弃某条轴上的⼀个或多个项很简单,只要有⼀个索引数组或列表即可。由于需要执⾏⼀些数据整理和集合逻辑,所以drop⽅法返回的是⼀个在指定轴上删除了指定值的新对象:
~Series

obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
obj
new_obj = obj.drop('c')
new_obj
obj.drop(['d', 'c'])

~DataFrame
对于DataFrame,可以删除任意轴上的索引值。

data = pd.DataFrame(np.arange(16).reshape((4, 4)),
                    index=['Ohio', 'Colorado', 'Utah', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
data

# ⽤标签序列调⽤drop会从⾏标签(axis 0)删除值:
data.drop(['Colorado', 'Ohio'])

# 通过传递axis=1或axis='columns'可以删除列的值:
data.drop('two', axis=1)
data.drop(['two', 'four'], axis='columns')

# drop会修改Series或DataFrame的⼤⼩或形状,
# 可以就地修改对象,不会返回新的对象:
obj.drop('c', inplace=True)
obj

3.2 算术运算和数据对齐

pandas最重要的⼀个功能是,它可以对不同索引的对象进⾏算术运算。在将对象相加时,如果存在不同的索引对,则结果的索引就是该索引对的并集。对于有数据库经验的⽤户,这就像在索引标签上进⾏⾃动外连接。
看⼀个简单的例⼦:

s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1],
               index=['a', 'c', 'e', 'f', 'g'])
s1
s2
s1 + s2

⾃动的数据对⻬操作在不重叠的索引处引⼊了NA值。缺失值会在算术运算过程中传播。
对于DataFrame,对⻬操作会同时发⽣在⾏和列上:

df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
                   index=['Ohio', 'Texas', 'Colorado'])
df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
                   index=['Utah', 'Ohio', 'Texas', 'Oregon'])
df1
df2
df1 + df2

如果DataFrame对象相加,没有共⽤的列或⾏标签,结果都会是空:

df1 = pd.DataFrame({'A': [1, 2]})
df2 = pd.DataFrame({'B': [3, 4]})
df1
df2
df1 - df2

3.2.1 在算数方法中填充值

在对不同索引的对象进⾏算术运算时,你可能希望当⼀个对象中某个轴标签在另⼀个对象中找不到时填充⼀个特殊值(⽐如0):

df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
                   columns=list('abcd'))
df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
                   columns=list('abcde'))
df2.loc[1, 'b'] = np.nan
df1
df2

# 将它们相加时,没有重叠的位置就会产⽣NA值:
df1 + df2

# 连接两个DataFrame:add
df1.add(df2, fill_value=0)

~Series和DataFrame的算术⽅法
在这里插入图片描述
它们每个都有⼀个副本,以字⺟r开头,它会翻转参数。因此这两个语句是等价的:

1 / df1
df1.rdiv(1)

与此类似,在对Series或DataFrame重新索引时,也可以指定⼀个填充值:

df1.reindex(columns=df2.columns, fill_value=0)

3.2.2 DataFrame和Series之间的运算

跟不同维度的NumPy数组⼀样,DataFrame和Series之间算术运算也是有明确规定的。先来看⼀个具有启发性的例⼦,计算⼀个⼆维数组与其某⾏之间的差:

arr = np.arange(12.).reshape((3, 4))
arr
arr[0]
arr - arr[0]

当我们从arr减去arr[0],每⼀⾏都会执⾏这个操作。这就叫做⼴播(broadcasting)。DataFrame 和Series之间的运算差不多也是如此:

frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
                     columns=list('bde'),
                     index=['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.iloc[0]
frame
series

默认情况下,DataFrame和Series之间的算术运算会将Series的索引匹配到DataFrame的列,然后沿着⾏⼀直向下⼴播:

frame - series

如果某个索引值在DataFrame的列或Series的索引中找不到,则参与运算的两个对象就会被重新索引以形成并集:

series2 = pd.Series(range(3), index=['b', 'e', 'f'])
frame + series2

如果你希望匹配⾏且在列上⼴播,则必须使⽤算术运算⽅法。例如:

series3 = frame['d']
frame
series3
frame.sub(series3, axis='index')

3.3 函数应用和映射

NumPy的ufuncs(元素级数组⽅法)也可⽤于操作pandas对象:

frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
                     index=['Utah', 'Ohio', 'Texas', 'Oregon'])
frame
np.abs(frame)

另⼀个常⻅的操作是,将函数应⽤到由各列或⾏所形成的⼀维数组上。DataFrame的apply⽅法即可实现此功能:

f = lambda x: x.max() - x.min()
frame.apply(f)
# 这⾥的函数f,计算了⼀个Series的最⼤和最⼩值的差,在frame的每列都执⾏了⼀次。
# 结果是⼀个Series,使⽤frame的列作为索引。

# 如果传递axis='columns'到apply,这个函数会在每⾏执⾏:
frame.apply(f, axis='columns')

许多最为常⻅的数组统计功能都被实现成DataFrame的⽅法(如sum和mean),因此⽆需使⽤apply⽅法。
传递到apply的函数还可以是以下情形:

# 传递到apply的函数不是必须返回⼀个标量,还可以返回由多个值组成的Series:
def f(x):
    return pd.Series([x.min(), x.max()], index=['min', 'max'])
frame.apply(f)

# 元素级的Python函数也是可以⽤的。
# 假如你想得到frame中各个浮点值的格式化字符串,使⽤applymap即可:
format = lambda x: '%.2f' % x
frame.applymap(format)

# 之所以叫做applymap,是因为Series有⼀个⽤于应⽤元素级函数的map⽅法
frame['e'].map(format) #与applymap意思一样,只不过只适用于Series

3.4 排序与排名

根据条件对数据集排序(sorting)也是⼀种重要的内置运算。
~根据索引排序

# 使⽤sort_index⽅法,它将返回⼀个已排序的新对象:
obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
obj.sort_index()

# 对于DataFrame,则可以根据任意⼀个轴上的索引进⾏排序:
frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
                     index=['three', 'one'],
                     columns=['d', 'a', 'b', 'c'])
frame.sort_index()
frame.sort_index(axis=1)

# 数据默认是按升序排序的,但也可以降序排序:
frame.sort_index(axis=1, ascending=False)

~根据值排序

# 若要按值对Series进⾏排序,可使⽤其sort_values⽅法
bj = pd.Series([4, 7, -3, 2])
obj.sort_values()

# 在排序时,任何缺失值默认都会被放到Series的末尾:
obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
obj.sort_values()

# 当排序⼀个DataFrame时,你可能希望根据⼀个或多个列中的值进⾏排序。
# 将⼀个或多个列的名字传递给sort_values的by选项即可达到该⽬的:
frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
frame
frame.sort_values(by='b')

# 要根据多个列进⾏排序,传⼊名称的列表即可:
frame.sort_values(by=['a', 'b'])

~rank方法
接下来介绍 Series和DataFrame的rank⽅法。

# 默认情况下,rank是通过“为各组分配⼀个平均排名”的⽅式破坏平级关系的:
obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
obj.rank()

# 也可以根据值在原数据中出现的顺序给出排名:
obj.rank(method='first')

# 你也可以按降序进⾏排名:
obj.rank(ascending=False, method='max')

# DataFrame可以在⾏或列上计算排名:
frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1],
                      'c': [-2, 5, 8, -2.5]})
frame
frame.rank(axis='columns')

排名时用于破坏平级关系的方法:
在这里插入图片描述

四、汇总和计算描述统计

4.1 汇总与描述统计

4.1.1 基本功能

pandas对象拥有⼀组常⽤的数学和统计⽅法。它们⼤部分都属于约简和汇总统计,⽤于从Series中提取单个值(如sum或 mean)或从DataFrame的⾏或列中提取⼀个Series。跟对应的NumPy数组⽅法相⽐,它们都是基于没有缺失数据的假设⽽构建的。看⼀个简单的DataFrame:

df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5],
                   [np.nan, np.nan], [0.75, -1.3]],
                  index=['a', 'b', 'c', 'd'],
                  columns=['one', 'two'])
df

# 调⽤DataFrame的sum⽅法将会返回⼀个含有列的和的Series:
df.sum()
df.sum(axis='columns')

# NA值会⾃动被排除,除⾮整个切⽚(这⾥指的是⾏或列)都是NA。
# 通过skipna选项可以禁⽤该功能:
df.mean(axis='columns', skipna=False)

~约简方法的选项
在这里插入图片描述
其他一些类型的方法:

# 有些⽅法返回的是间接统计(⽐如达到最⼩值或最⼤值的索引):
df.idxmax()
df.idxmin()

# 另⼀些⽅法则是累计型的:
df.cumsum()

# 还有⼀种⽅法,它既不是约简型也不是累计型。
# describe就是⼀个例⼦,它⽤于⼀次性产⽣多个汇总统计:
obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
obj.describe()

~描述和汇总统计
在这里插入图片描述

4.1.2 协方差与相关系数

有些汇总统计(如相关系数和协⽅差)是通过参数对计算出来的。

df = pd.DataFrame([[1.4, 6.2], [7.1, -4.5],
                   [4.3, 1.2], [0.75, -1.3]],
                  index=['a', 'b', 'c', 'd'],
                  columns=['one', 'two'])
s1 = df.one
s2 = df.two
df

# Series的cov、corr方法
s1.cov(s2)
s1.corr(s2)

# DataFrame的cov、corr方法
# 以DataFrame的形式分别返回完整的相关系数或协⽅差
df.cov()
df.corr()

# DataFrame的corrwith方法
# 可以计算其列或⾏跟另⼀个Series或DataFrame之间的相关系数
df.corrwith(s1)
df.corrwith(s1, axis='index')

# 传⼊axis='columns'即可按⾏进⾏计算
# ⽆论如何,在计算相关系数之前,所有的数据项都会按标签对⻬
df.corrwith(s1, axis='index')

4.2 唯一值、值计数以及成员资格

还有⼀类⽅法可以从⼀维Series的值中抽取信息。看下⾯的例⼦:

obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])

# unique函数(它可以得到Series中的唯⼀值数组)
	# 返回的唯⼀值是未排序的,如果需要的话,可以对结果再次进⾏排序(uniques.sort())。
uniques = obj.unique()
uniques

# value_counts函数(⽤于计算⼀个 Series中各值出现的频率)
	# 为了便于查看,结果Series是按值频率降序排列的
	# value_counts还是⼀个顶级pandas⽅法,可⽤于任何数组或序列
obj.value_counts()
pd.value_counts(obj.values, sort=False)

# isin函数(判断⽮量化集合的成员资格)
	# 可⽤于过滤Series中或DataFrame列中数据的⼦集
obj
mask = obj.isin(['b', 'c'])
mask
obj[mask]

# Index.get_indexer⽅法
	# 它可以给你⼀个索引数组,从可能包含重复值的数组到另⼀个不同值的数组
to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a'])
unique_vals = pd.Series(['c', 'b', 'a'])
pd.Index(unique_vals).get_indexer(to_match)

~唯一值、值计数、成员资格方法
在这里插入图片描述
有时,你可能希望得到DataFrame中多个相关列的⼀张柱状图。 例如:

data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4],
                     'Qu2': [2, 3, 1, 2, 3],
                     'Qu3': [1, 5, 2, 4, 4]})
data
# 将pandas.value_counts传给该DataFrame的apply函数,就会出现:
result = data.apply(pd.value_counts).fillna(0)
result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值