Pandas概述
Pandas(Python Data Analysis Library )是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。Pandas提供了大量能使我们快速便捷地处理数据的函数和方法。你很快就会发现,它是使Python成为强大而高效的数据分析环境的重要因素之一。
Pandas是Python的一个数据分析包,最初由AQR Capital Management于2008年4月开发,并于2009年底开源出来,目前由专注于Python数据包开发的PyData开发team继续开发和维护,属于PyData项目的一部分。Pandas最初被作为金融数据分析工具而开发出来。
Pandas含有使数据分析工作变得更快更简单的高级数据结构和操作工具。pandas是基于Numpy构建的,让以Numpy为中心的应用变得更简单。
Pandas专用于数据预处理和数据分析的Python第三方库,最适合处理大型结构化表格数据
- Pandas是2008年Wes McKinney于AQR资本做量化分析师时创建
- Pandas借鉴了R的数据结构
- Pandas基于Numpy搭建,支持Numpy中定义的大部分计算
- Pandas含有使数据分析工作更简单高效的高级数据结构和操作工具
- Pandas底层用Cython和C做了速度优化,极大提高了执行效率
Pandas引入约定:
from pandas import Series, DataFrame
import pandas as pd
Python、Numpy和Pandas对比
Python
- list:Python自带数据类型,主要用一维,功能简单,效率低
- Dict:Python自带数据类型,多维键值对,效率低
Numpy
- ndarray:Numpy基础数据类型,单一数据类型
- 关注数据结构/运算/维度(数据间关系)
Pandas
- Series:1维,类似带索引的1维ndarray
- DataFrame:2维,表格型数据类型,类似带行/列索引的2维ndarray
关注数据与索引的关系(数据实际应用)
从实用性、功能强弱和和可操作性比较:list < ndarray < Series/DataFrame
数据规整和分析工作中,ndarry数组作为必要补充,大部分数据尽量使用Pandas数据类型.
Pandas数据结构
Pandas的核心为两大数据结构,数据分析相关所有事物都是围绕着这两种结构进行的:
- Series
- DataFrame
Series这样的数据结构用于存储一个序列的一维数据,而DataFrame作为更复杂的数据结构,则用于存储多维数据。
虽然这些数据结构不能解决所有的问题,但它们为大多数应用提供了有效和强大的工具。就简洁性而言,他们理解和使用起来都很简单。
两种数据结构奇特之处是讲索引对象和标签整合到自己的结构中,使得两种数据结构都具有很强的可操作性.
Series
Series是一种类似于一维数组的对象,它由一组数据(各种Numpy数据类型)以及一组与之相关的数据标签(即索引)组成。仅由一组数据即可产生最简单的Series.
Series示例
Series的字符串表现形式为:索引在左边,值在右边。由于我们没有为数据指定索引,于是会自动创建一个0~N-1的整数索引。我们可以通过Series的index和values属性获取索引和值的数组表现形式。
import pandas as pd
pd.Series([i for i in range(1,6)])
输出结果为:
0 1
1 2
2 3
3 4
4 5
dtype: int64
Series基本操作
import pandas as pd
s1 = pd.Series([i for i in range(1,6)])
- 访问所有的索引
s1.index
RangeIndex(start=0, stop=5, step=1)
- 访问所有的值
s1.values
array([1, 2, 3, 4, 5])
- 传入ndarray
s2 = pd.Series(np.arange(10))
s2
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
dtype: int64
- 传入字典
s3 = pd.Series({'1': 1, '2': 2, '3': 3})
s3
1 1
2 2
3 3
dtype: int64
s3.index
Index(['1', '2', '3'], dtype='object')
s3.values
array([1, 2, 3])
- 指定index
s4 = pd.Series([1, 2, 3, 4], index=['A', 'B', 'C', 'D'])
s4
A 1
B 2
C 3
D 4
dtype: int64
s4.values
array([1, 2, 3, 4])
s4.index
Index(['A', 'B', 'C', 'D'], dtype='object')
- Series的访问
s4['A']
1
- Series的筛选
s4[s4>2]
C 3
D 4
dtype: int64
- 转化为字典
s4.to_dict()
{'A': 1, 'B': 2, 'C': 3, 'D': 4}
# 如果只传入一个字典,则结果Series中的索引就是原字典的键(有序排列)。你可以传入排好序的字典的键以改变顺序:
s5 = pd.Series(s4.to_dict())
s5
A 1
B 2
C 3
D 4
dtype: int64
index_1 = ['A', 'B', 'C','D','E']
s6 = pd.Series(s5, index = index_1)
s6 #由于E这个索引没有对应的值,即为NaN(即“非数字”(not a number),在pandas中,它用于表示缺失或NA值
A 1.0
B 2.0
C 3.0
D 4.0
E NaN
dtype: float64
- 我将使用缺失(missing)或NA表示缺失数据。pandas的isnull和notnull函数可用于检测缺失数据:
pd.isnull(s6)
A False
B False
C False
D False
E True
dtype: bool
# Series也有类似的实例方法, 面向对象调用
s6.isnull()
A False
B False
C False
D False
E True
dtype: bool
pd.notnull(s6)
A True
B True
C True
D True
E False
dtype: bool
- Series对象本身及其索引都有一个name属性,该属性跟pandas其他的关键功能关系非常密切:
s6.name = 'DEMO1'
s6
A 1.0
B 2.0
C 3.0
D 4.0
E NaN
Name: DEMO1, dtype: float64
s6.index.name = 'demo_index'
s6
demo_index
A 1.0
B 2.0
C 3.0
D 4.0
E NaN
Name: DEMO1, dtype: float64
- Series的索引可以通过赋值的方式就地修改:
s6.index
Index(['A', 'B', 'C', 'D', 'E'], dtype='object', name='demo_index')
import string
up_index = [i for i in string.ascii_lowercase[21:26]]
up_index
['v', 'w', 'x', 'y', 'z']
s6.index = up_index
s6
v 1.0
w 2.0
x 3.0
y 4.0
z NaN
Name: DEMO1, dtype: float64
Series操作
选取操作
Series对象支持通过以下方式查询数据:
- 位置下标 2. 标签索引3. 切片索引 4. 布尔型索引
from pandas import Series
import pandas as pd
series1 = Series([10, 20, 30, 40], index=list('abcd'))
# 通过位置查询
series1[2]
30
# 通过标签索引查询
series1['b']
20
# 查询多个元素
series1[[0, 2, 3]]
series1[['a', 'c', 'd']]
a 10
c 30
d 40
dtype: int64
# 切片
series1[1:3]
series1['b':'d']
b 20
c 30
d 40
dtype: int64
# 布尔索引
series1[series1 > 20]
c 30
d 40
dtype: int64
- 删除操作
我们队对Series中的元素执行删除,主要用到Series.drop函数和pop函数.drop删除数据之后,返回删除后的副本.Series.pop在删除源数据的元素
series1 = Series([10, 20, 30, 40], index=list('abcd'))
series1
a 10
b 20
c 30
d 40
dtype: int64
# 删除元素返回副本
series1.drop('c')
a 10
b 20
d 40
dtype: int64
series1.drop(['a', 'd'])
b 20
c 30
dtype: int64
# 删除源数据中的元素
series1.pop('d')
40
- 插入操作
series1 = Series([10, 20, 30, 40], index=list('abcd'))
series2 = Series([100, 200], index=['g', 'h'])
# 新增一个标签索引值为f,值为100的元素
series1['f'] = 100
series1.append(series2)
a 10
b 20
c 30
d 40
f 100
g 100
h 200
dtype: int64
- Series运算
import numpy as np
series1 = Series([10, 20, 30, 40], index=list('abcd'))
series2 = Series([1, 2, 3, 4], index=list('abce'))
series1 * 2
a 20
b 40
c 60
d 80
dtype: int64
# 两个series运算,会自动对齐运算
series1 * series2
a 10.0
b 40.0
c 90.0
d NaN
e NaN
dtype: float64
# 使用Numpy函数
np.sum(series1)
100
# 不对齐运算,将series当做numpy运算
np.add(series1, series2)
a 11
b 22
c 33
d 44
dtype: int64
np.greater(series1, series2)
a True
b True
c True
d True
dtype: bool
DataFrame
DataFrame这种列表式的数据结构和Excel工作表非常类似,其设计初衷是将Series的使用场景由一维扩展到多维. DataFrame由按一定顺序的多列数据组成,各列的数据类型可以有所不同(数值、字符串、布尔值).
Series对象的Index数组存放有每个元素的标签,而DataFrame对象有所不同,它有两个索引数组。第一个索引数组与行有关,它与Series的索引数组极为相似。 每个标签与标签所在行的所有元素相关联。而第二个数组包含一系列标签,每个标签与一列数据相关联.
DataFrame还可以理解为一个由Series组成的字典,其中每一列的列名为字典的键,每一个Series作为字典的值.
创建DataFrame对象最常用的方法是传递一个dict对象给DataFrame构造函数。DataFrame以dict的键作为列名,每个键都有一个数组作为值. 也可以通过嵌套的dict、list来创建DataFrame对象.
dataframe的创建
- 通过字典来创建DataFrame
from pandas import DataFrame, Series
import pandas as pd
persons = {
'name': ['小睿', '小丽', '小明', '小红'],
'age': [19, 18, 18, 17],
'sex': ['男', '男', '女', '男'],
}
# 字典的key作为列索引
data_frame1 = DataFrame(persons)
data_frame1
contries = {
'中国': {'2013': 10, '2014': 20, '2015': 30},
'阿富汗': {'2013': 12, '2014': 25, '2015': 33},
'新加坡': {'2013': 11, '2014': 22, '2015': 38},
'柬埔寨': {'2013': 18, '2014': 16, '2015': 27},
}
# 外层key做列索引,内层key做行索引
data_frame3 = DataFrame(contries)
data_frame3
- 通过二维Numpy数组创建DataFrame
import numpy as np
ndarray1 = np.random.randint(1, 10, (3, 4))
# 使用默认行索引和列索引
data_frame2 = DataFrame(ndarray1)
data_frame2
- 通过列表创建DataFrame
data_frame3 = DataFrame([1, 2, 3, 4, 5])
data_frame4 = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
data_frame3
data_frame4
DataFrame索引
# 打印DataFrame行索引
contries = {
'俄罗斯': {'2013': 10, '2014': 20, '2015': 30},
'阿富汗': {'2013': 12, '2014': 25, '2015': 33},
'新加坡': {'2013': 11, '2014': 22, '2015': 38},
'柬埔寨': {'2013': 18, '2014': 16, '2015': 27},
}
# 外层key做列索引,内层key做行索引
data_frame5 = DataFrame(contries)
data_frame5
- 行索引
# 行索引
data_frame5.index
Index(['2013', '2014', '2015'], dtype='object')
- 列索引
# 列索引
data_frame5.columns
Index(['俄罗斯', '阿富汗', '新加坡', '柬埔寨'], dtype='object')
- 修改行索引
# 修改行索引
data_frame5.index = ['aaa', 'bbb', 'ccc']
data_frame5
# 修改列索引
data_frame5.columns = ['一', '二', '三', '四']
data_frame5
Dataframe的操作
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
contries = {
'中国': {'2013': 10, '2014': 20, '2015': 30},
'阿富汗': {'2013': 12, '2014': 25, '2015': 33},
'新加坡': {'2013': 11, '2014': 22, '2015': 38},
'柬埔寨': {'2013': 18, '2014': 16, '2015': 27},
}
# 外层key做列索引,内层key做行索引
data_frame1 = DataFrame(contries)
data_frame1
- 获取元素 获取列数据
# 获取列数据
data_frame1['阿富汗']
返回了一个series
2013 12
2014 25
2015 33
Name: 阿富汗, dtype: int64
# 获取指定多列数据
data_frame1[['柬埔寨', '中国', '阿富汗']]
对于DataFrame的行的标签索引,我引入了特殊的标签运算符loc和iloc。它们可以让你用类似NumPy的标记,使用轴标签(loc)或整数索引(iloc),从DataFrame选择行和列的子集。
- loc 使用标签索引 loc(行,列)
- iloc 只能使用整数作为索引 iloc(行,列)
# 获得列切片数据
data_frame1.iloc[:, 1: 3]
# 获取行数据, loc里面可以放字符串标签和布尔值、数组
data_frame1.loc['2014']
中国 20
阿富汗 25
新加坡 22
柬埔寨 16
Name: 2014, dtype: int64
# 获取多行数据
data_frame1.loc[['2013', '2014']]
data_frame1.loc[['2013', '2014'], ['阿富汗','柬埔寨']]
# 查找阿富汗大于15的行
data_frame1.loc[data_frame1.阿富汗 > 15]
# 查找中国大于12的行,保留中国 阿富汗 两列
data_frame1.loc[data_frame1.中国 > 12, ['阿富汗', '中国']]
# 位置下标获取行数据
data_frame1.iloc[0]
中国 10
阿富汗 12
新加坡 11
柬埔寨 18
Name: 2013, dtype: int64
# 切片获得区间数据,以下两种效果一样
data_frame1.iloc[1:]
data_frame1[1:]
# 数组切片索引
data_frame1.iloc[1:, [0, 1]]
# 数组切片索引
data_frame1.iloc[1:, 1:]
Dataframe插入数据
contries = {
'中国': {'2013': 10, '2014': 20, '2015': 30},
'阿富汗': {'2013': 12, '2014': 25, '2015': 33},
'新加坡': {'2013': 11, '2014': 22, '2015': 38},
'柬埔寨': {'2013': 18, '2014': 16, '2015': 27},
}
# 外层key做列索引,内层key做行索引
data_frame2 = DataFrame(contries)
# 插入一行数据
series1 = Series([100, 200, 300, 400], index=['阿富汗', '柬埔寨', '新加坡', '中国'], name='2019')
series1
阿富汗 100
柬埔寨 200
新加坡 300
中国 400
Name: 2019, dtype: int64
data_frame2.loc['2016'] = 66
data_frame2.loc['2017'] = [23, 24, 25, 26]
data_frame2.loc['2018'] = series1
# append调用之后会产生数据副本
data_frame3 = data_frame2.append(series1)
data_frame3
# 在最后一列增加一列新数据
data_frame3['法兰西'] = Series(np.arange(7), index=[str(x) for x in range(2013, 2020)])
# 在指定位置增加一列
data_frame3.insert(2, '马来西亚', Series(np.random.randint(0, 7, 7), index=[str(x) for x in range(2013, 2020)]))
data_frame3.insert(4, '印度', np.random.randint(0, 7, 7))
data_frame3
Dataframe中删除数据
contries = {
'中国': {'2013': 10, '2014': 20, '2015': 30},
'阿富汗': {'2013': 12, '2014': 25, '2015': 33},
'新加坡': {'2013': 11, '2014': 22, '2015': 38},
'柬埔寨': {'2013': 18, '2014': 16, '2015': 27},
'印度': {'2013': 18, '2014': 16, '2015': 27},
'锡金': {'2013': 18, '2014': 16, '2015': 27},
'法兰西': {'2013': 18, '2014': 16, '2015': 27},
}
# 外层key做列索引,内层key做行索引
data_frame1 = DataFrame(contries)
data_frame1
# 删除一列数据
del data_frame1['法兰西']
原地操作
data_frame1
原地操作
data_frame1.pop('中国')
2013 10
2014 20
2015 30
Name: 中国, dtype: int64
data_frame1
# 使用drop方法删除多列数据
data_frame2 = data_frame1.drop('锡金', axis=1) # 0跨行 1跨列
data_frame2 = data_frame1.drop(['新加坡', '印度'], axis=1) # 0跨行 1跨列
# data_frame2 = data_frame1.drop(['2014', '2015'], axis=0) # 删除行
data_frame2
关于axis轴的详细介绍请点击https://www.jianshu.com/p/6a97a3ef183a
DataFrame算数运算和数据对齐
pandas能将两个数据结构的索引对齐,这可能是与pandas数据结构索引有关的最强大的功能。这一点尤其体现在数据结构之间的算数运算上.参与运算的两个数据结构,其索引顺序可能不一致,而且有的索引项可能只存在一个数据结构中.
- Series之间运算,遵循索引对齐
from pandas import Series, DataFrame
import pandas as pd
series1 = Series([10, 20, 30, 40], index=list('abcd'))
series2 = Series([20, 30, 40, 50], index=list('bcde'))
# 如果索引并不重叠,则使用NaN
series1 + series2
a NaN
b 40.0
c 60.0
d 80.0
e NaN
dtype: float64
- DataFrame运算会对齐行和列索引
from pandas import Series, DataFrame
import pandas as pd
import numpy as np
data_frame1 = DataFrame(
np.arange(12).reshape((3, 4)),
index=['fir', 'sec', 'thr'],
columns=['a', 'b', 'c', 'd']
)
data_frame2 = DataFrame(
np.arange(8, 20).reshape((4, 3)),
index=['fir', 'sec', 'thr', 'for'],
columns=['a', 'b', 'c']
)
data_frame1
data_frame2
data_frame1 + data_frame2
data_frame1.add(data_frame2)
# 在算数方法中填充值,当某个标签在另一个对象中找不到时填充一个值。
data_frame1.add(data_frame2, fill_value=0)
- DataFrame和Series之间的运算
import numpy as np
data_frame2 = DataFrame(np.arange(9).reshape((3, 3)),
index=['a', 'b', 'c'],
columns=['num1', 'num2', 'num3'])
series2 = Series([10, 20, 30, 40],
index=['num1', 'num2', 'num3', 'num4'])
data_frame2
series2
num1 10
num2 20
num3 30
num4 40
dtype: int64
# DataFrame每一行都加上series
data_frame2 + series2
# DataFrame每一列加上series
series3 = Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
data_frame2.add(series3, axis=0)
pandas中函数应用apply
pandas库以Numpy为基础,并对它的很多功能进行了扩展,用来操作新的数据结构Series和DataFrame.通用函数(ufunc, universal function)就是扩展得到的功能,这类函数能够对数据结构中的元素进行操作,特别有用.除了通用函数,用户还可以自定义函数。
数组中的大多数函数对DataFrame依然有效,因此没有必须要使用apply函数.
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame1 = DataFrame(np.arange(-8, 8).reshape((4, -1)))
data_frame1
# 计算每一个元素的平方
np.square(data_frame1)
- 将函数应用到各行或各列所形成的一维数组上
def apply_function(x):
return np.sum(x)
# 将每一列数据应用到apply_function上
data_frame1.apply(apply_function)
0 -8
1 -4
2 0
3 4
dtype: int64
# 将每一行数据应用到apply_function上
data_frame1.apply(apply_function, axis=1)
0 -26
1 -10
2 6
3 22
dtype: int64
# applymap可以将每一个元素应用到函数中
data_frame1.applymap(lambda x: x + 100)
Pandas排序
根据条件对数据集进行排序也是一种重要的内置运算. 要对行或列索引进行排序,可使用sort_index方法,要根据元素值来排序,可使用sort_values.
def sort_index(self, axis=0, ascending=True, inplace=False):
pass
def sort_values(self, by, axis=0, ascending=True, inplace=False):
pass
axis: 按行还是按列排序.
ascending: 升序还是降序,默认True是升序排列,False降序排序.
inplace: 默认False, 返回排序副本. True表示直接修改序列本身
对Series排序
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
series1 = Series(np.random.randint(1, 100, 4), index=['d', 'a', 'c', 'b'])
series1
d 81
a 85
c 33
b 44
dtype: int64
# 根据索引升序排列
series1.sort_index()
a 85
b 44
c 33
d 81
dtype: int64
# 根据索引降序排列
series1.sort_index(ascending=False, inplace=True)
series1
d 81
c 33
b 44
a 85
dtype: int64
# 按照值对Series升序排列
series1.sort_values()
c 33
b 44
d 81
a 85
dtype: int64
# 按照值对Series降序排列
series1.sort_values(ascending=False)
a 85
d 81
b 44
c 33
dtype: int64
- 对DataFrame排序
data_frame1 = DataFrame(
np.arange(16).reshape((4, 4)),
index=['b', 'a', 'd', 'c'],
columns=[4, 2, 3, 1]
)
data_frame1
# 根据行索引进行排序
data_frame1.sort_index()
# 根据行索引进行降序排列
data_frame1.sort_index(ascending=False)
# 根据列索引排序
data_frame1.sort_index(axis=1)
# 根据列索引降序排列
data_frame1.sort_index(ascending=False, axis=1, inplace=True)
data_frame1
# 按照值对行进行升序排列
data_frame1.sort_values(by=1)
# 按照值对行进行降序排列
data_frame1.sort_values(by=1, ascending=False)
# 按照值对指定列进行升序排列
data_frame1.sort_values(by='a', axis=1)
# 按照值对指定列进行降序排列
data_frame1.sort_values(by='a', axis=1, ascending=False)
等级索引和分级
等级索引是pandas的一个重要功能,单条轴可以有多级索引. 你可以像操作两位结构那样处理多维数据.
from pandas import Series, DataFrame
import pandas as pd
import numpy as np
areas_economy = Series(
np.arange(10, 100, 10),
index=[
['北京市', '北京市', '北京市', '河北省', '河北省', '河北省', '山东省', '山东省', '山东省'],
['信息业', '制造业', '服务业', '信息业', '制造业', '服务业', '信息业', '制造业', '服务业']])
areas_economy
北京市 信息业 10
制造业 20
服务业 30
河北省 信息业 40
制造业 50
服务业 60
山东省 信息业 70
制造业 80
服务业 90
dtype: int64
- 层级索引取值和索引sort_index排序
# 1. 获得一个省的信息
areas_economy['山东省']
信息业 70
制造业 80
服务业 90
dtype: int64
# 2. 获得多个省信息
areas_economy[['河北省', '山东省']]
河北省 信息业 40
制造业 50
服务业 60
山东省 信息业 70
制造业 80
服务业 90
dtype: int64
# 3. 获得所有省的制造业信息
areas_economy[:, '制造业']
# 获得多个省制造业信息错误写法
# areas_economy[['河北省', '山东省'], '制造业']
北京市 20
河北省 50
山东省 80
dtype: int64
# 4. 获得多个省制造业信息
areas_economy.loc[['河北省', '山东省'], '制造业']
河北省 制造业 50
山东省 制造业 80
dtype: int64
# 5. 获得多个省制造业、服务业信息
areas_economy.loc[['河北省', '山东省'], ['制造业', '服务业']]
河北省 制造业 50
服务业 60
山东省 制造业 80
服务业 90
dtype: int64
# 需要对最外层排序之后才可执行切片索引
areas_economy.sort_index()['北京市':'山东省']
北京市 信息业 10
制造业 20
服务业 30
山东省 信息业 70
制造业 80
服务业 90
dtype: int64
# 交换索引
areas_economy.swaplevel()
信息业 北京市 10
制造业 北京市 20
服务业 北京市 30
信息业 河北省 40
制造业 河北省 50
服务业 河北省 60
信息业 山东省 70
制造业 山东省 80
服务业 山东省 90
dtype: int64
# 对索引进行排序, 可通过level=0或1指定根据那层索引排序
areas_economy.sort_index(level=0)
北京市 信息业 10
制造业 20
服务业 30
山东省 信息业 70
制造业 80
服务业 90
河北省 信息业 40
制造业 50
服务业 60
dtype: int64
- 按层级统计数据
# 计算北京市经济总量
areas_economy['北京市'].sum()
# 计算山东省三大行业的平均经济总量
areas_economy['山东省'].mean()
80.0
# 统计每个行业的经济总量
areas_economy.sum(level=1)
信息业 120
制造业 150
服务业 180
dtype: int64
data_frame1 = DataFrame(
np.arange(0, 360, 10).reshape((6, 6)),
index=[
['北京市', '北京市', '河北省', '河北省', '河南省', '河南省'],
['昌平区', '海淀区', '石家庄', '张家口', '驻马店', '平顶山'],
],
columns=[
['轻工业', '轻工业', '重工业', '重工业', '服务业', '服务业'],
['纺织业', '食品业', '冶金业', '采煤业', '教育业', '游戏业'],
]
)
data_frame1
data_frame1['重工业']
data_frame1.loc['河北省']
data_frame1.loc['河北省']['重工业']
处理缺失数据
缺失数据(missing data)在大部分数据分析应用中都很常见。补上缺失值非常用,它们在数据结构中用NaN来表示,便于识别.
处理缺失值一般有三种方式
- 直接丢弃数据中的包含NaN的行或列.
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame1 = DataFrame([
[10, 20, 30, 40, 50],
[11, 21, np.nan, 41, np.nan],
[12, 22, 32, 42, 52],
[np.nan, 66, np.nan, np.nan, np.nan],
[np.nan, 24, np.nan, 44, 54],
])
data_frame1
# 默认丢弃包含NaN的行
data_frame1.dropna()
# 丢弃包含NaN的列
data_frame1.dropna(axis=1)
# 如果只是想删除所有元素是NaN的行或列, 需要参数how='all'
data_frame1.dropna(how='all', axis=1)
- 在选取数据的时候过滤掉包含NaN的行或列.
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame1 = DataFrame([
[10, 20, 30, 40, 50],
[11, 21, np.nan, 41, np.nan],
[12, 22, 32, 42, 52],
[np.nan, 66, np.nan, np.nan, np.nan],
[np.nan, 24, np.nan, 44, 54],
])
data_frame1
# 获得第一列不为NaN的元素
data_frame1[0][data_frame1[0].notnull()]
0 10.0
1 11.0
2 12.0
Name: 0, dtype: float64
# 获得第三行不为NaN的元素
data_frame1.loc[3][~(data_frame1.loc[3].isnull())]
data_frame1.loc[3][data_frame1.loc[3].notnull()]
1 66.0
Name: 3, dtype: float64
- 填充NaN值
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame1 = DataFrame([
[10, 20, 30, 40, 50],
[11, 21, np.nan, 41, np.nan],
[12, 22, 32, 42, 52],
[np.nan, 66, np.nan, np.nan, np.nan],
[np.nan, 24, np.nan, 44, 54],
])
data_frame1
data_frame1.fillna(666)
# 每一列替换为不同的值, 目前不支持对按行填充不同值的功能.
data_frame1.fillna({0: 100, 1: 200, 2:300, 3: 400, 4: 500})
数据准备
我们从文件或数据库等数据源获取数据之后,将数据转换为DataFrame格式后,我们就可以对数据进行处理了。数据处理的目的是准备数据,便于我们后续分析数据。
- 我们下面将会深入学习pandas库在数据处理阶段的功能. 数据处理分为三个阶段:
1.数据准备
2.数据转换
3.数据聚合
- 开始处理数据工作之前,需要先行准备好数据,把数据组装成便于用pandas库的各种工具处理的数据结构。数据准备阶段包括以下步骤:
1.数据加载 2. 组装:合并(merging)拼接(concatenation)组合(combine)3.变形(轴向旋转)
数据加载就是从文件、数据库等数据源中讲待处理的数据加载到程序中,并转换为DataFrame等结构。数据可能来自不同的数据源,有着不同的格式,需要将其归并为一个DataFrame, 然后才能做进一步的处理.
数据组装
对于存储在pandas对象中的各种数据,组装的方法有以下几种:
- 合并--pandas.merge()函数根据一个或多个键连接多行.
- 拼接--pandas.concat()函数按照轴把多个对象拼接起来.
- 结合--pandas.DataFrame.combine_first()函数从另外一个数据结构获取数据,连接重合的数据,填充缺失值.
合并(merge)
对于合并操作,熟悉SQL的读者可以将其理解为JOIN操作,它使用一个或多个键把多行数据结合在一起.
Merge通过索引来合并数组
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame1 = DataFrame({
'name': ['John', 'Edward', 'Smith', 'Obama', 'Clinton' ],
'ages1': [18, 16, 15, 14, 19]
})
data_frame2 = DataFrame({
'name': ['John', 'Edward', 'Polly', 'Obama' ],
'ages2': [19, 16, 15, 14]
})
data_frame3 = DataFrame({
'other_name': ['John', 'Edward', 'Smith', 'Obama', 'Bush' ],
'other_ages': [18, 16, 15, 14, 20]
})
print(data_frame1)
print('-'*10)
print(data_frame2)
print('-'*10)
print(data_frame3)
name ages1
0 John 18
1 Edward 16
2 Smith 15
3 Obama 14
4 Clinton 19
----------
name ages2
0 John 19
1 Edward 16
2 Polly 15
3 Obama 14
----------
other_name other_ages
0 John 18
1 Edward 16
2 Smith 15
3 Obama 14
4 Bush 20
# 默认根据相同列名作为键连接两个DataFrame, 并且内连接方式(inner, 取两边都有的数据, 交集)
# 那么请问, 当我将data_frame2的列名ages2改为ages1时,结果是多少?
pd.merge(data_frame1, data_frame2, on='name')
# 可修改连接方式为外连接(outer),取两遍都有数组, 缺失数据使用NaN表示
pd.merge(data_frame1, data_frame2, how='outer')
# 当两个DataFrame没有公共相同的列名的时候,那么合并就会报错.
# MergeError: No common columns to perform merge on
# 此时我们可指定DataFrame用那一列名来连接另一个DataFrame的那个列名
# 使用参数 left_on 和 right_on
pd.merge(data_frame1, data_frame3, left_on='name', right_on='other_name')
# 还可以在此基础上使用外连接
pd.merge(data_frame1, data_frame3, left_on='name', right_on='other_name', how='outer')
# 除了内外连接,还有左连接(left)和右连接(right)
# 左连接以左DataFrame为准, 右DataFrame不存在的列用NaN代替.
pd.merge(data_frame1, data_frame2, how='left')
# 左连接以右DataFrame为准, 左DataFrame不存在的列用NaN代替.
pd.merge(data_frame1, data_frame2, how='right')
# 有时候,DataFrame中连接的键在索引(index)中。
# 在这种情况下传入left_index=True, right_index=True
# 说明索引被应用于连接键.
pd.merge(data_frame1, data_frame2, left_index=True, right_index=True, suffixes=['_a', '_b'])
拼接(concat)
concat函数可以将DataFrame、Series根据不同的轴作简单的融合
pd.concat(objs, axis=0, join='outer')
参数说明:objs: series,dataframe构成的序列lsit axis: 需要合并链接的轴,0是行,1是列 join:连接的方式 inner,或者outer
from pandas import DataFrame, Series
import pandas as pd
df1 = DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']},
index=[0, 1, 2, 3])
df2 = DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
'B': ['B4', 'B5', 'B6', 'B7'],
'C': ['C4', 'C5', 'C6', 'C7'],
'D': ['D4', 'D5', 'D6', 'D7']},
index=[4, 5, 6, 7])
df3 = DataFrame({'A': ['A8', 'A9', 'A10', 'A11'],
'B': ['B8', 'B9', 'B10', 'B11'],
'C': ['C8', 'C9', 'C10', 'C11'],
'D': ['D8', 'D9', 'D10', 'D11']},
index=[8, 9, 10, 11])
df4 = pd.DataFrame({'B': ['B2', 'B3', 'B6', 'B7'],
'D': ['D2', 'D3', 'D6', 'D7'],
'F': ['F2', 'F3', 'F6', 'F7']},
index=[2, 3, 6, 7])
result = pd.concat([df1, df2, df3])
# 默认是将后者DataFrame向纵方向拼接.
result
# 上面拼接完成之后,我们不清楚在Result中哪些数据是data_frame1
# 哪些数据是data_frame1, 我们可以通过keys为其增加等级索引
result = pd.concat([df1, df2, df3], keys=['x', 'y', 'z'])
result
下面我们看看如何横向拼接两个DataFrame:
- 横向拼接,我们只需要将设置axis=1.
result = pd.concat([df1, df4], axis=1)
result
我们下面看看join如何用, join有两个可选项, 默认是outer(两个DataFrame并集), 也可设置为inner(两个DataFrame交集).
result = pd.concat([df1, df4], axis=1, join='inner')
result
组合(combine)
假如我们有索引全部或部分重叠的两个数据集. 我们可能希望用一个数据集中的元素去补充另外一个数据集中的元素.例如:
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
series1 = Series([np.nan, 10, 20, np.nan, 30], index=list('abcde'))
series2 = Series([100, 11, 22, 200, np.nan], index=list('abcde'))
series1
# a NaN
# b 10.0
# c 20.0
# d NaN
# e 30.0
# dtype: float64
series2
# a 100.0
# b 11.0
# c 22.0
# d 200.0
# e NaN
# dtype: float64
# 如果series1中的元素为NaN,那么用series2中的元素来补充
# 如果series2中的元素在series1中不存在, 则在series1中新增元素
series1.combine_first(series2)
# a 100.0
# b 10.0
# c 20.0
# d 200.0
# e 30.0
# dtype: float64
a 100.0
b 10.0
c 20.0
d 200.0
e 30.0
dtype: float64
轴向旋转
我们除了要将来自不同数据源的进行整合统一之外,另外一种常用操作成为轴向旋转(pivoting). 轴向旋转操作我们主要用到两个函数:
1.入栈(stacking):旋转数据结构,把列转换为行.
2.出栈(unstacking): 把行转换为列
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame = DataFrame(
np.arange(9).reshape((3, 3)),
index=list('abc'),
columns=list('efg')
)
data_frame
# 将data_frame的列['e', 'f', 'g']转换为行
data_frame.stack()
a e 0
f 1
g 2
b e 3
f 4
g 5
c e 6
f 7
g 8
dtype: int64
# 将行转换为列
data_frame.stack().unstack()
# 将行转换为列
# 0表示将['a', 'b', 'c']转换为列
# 1表示将['e', 'f', 'g']转换为列
data_frame.stack().unstack(0)
数据转换
我们已经学习了如何准备数据以便于进行数据分析. 这个过程体现在重组DataFrame中的数据上.现在我们进行第二步数据转换.
在数据转换过程中,有些操作会涉及重复或无效元素,可能需要将其删除或者替换为别的元素.
删除重复行
数据处理的最后一步是删除多余的列和行.前面我们已经学习了del、DataFrame.drop()删除行列.由于多种原因,DataFrame对象可能包含重复的行.在大型的DataFrame中,检测重复的行可能遇到各种问题。pandas为此提供了多种工具,便于分析大型数据中的重复数据.
这里我们主要学习连个用于检测和删除重复行的函数
1.duplicated函数,用于检测重复行,返回元素为Bool类型的Series对象. 2.drop_duplicates, 用于从DataFrame对象中删除重复的行.
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame = DataFrame({
'name': ['John', 'John', 'Edward', 'Smith', 'Edward', 'John'],
'age': [19, 19, 20, 21, 20, 19]
})
data_frame
# 检测是否存在重复行
data_frame.duplicated()
0 False
1 True
2 False
3 False
4 True
5 True
dtype: bool
# 删除重复行
data_frame.drop_duplicates()
映射相关
pandas提供了几个利用映射关系来实现某些操作的函数。映射关系无非就是创建一个映射关系的列表,把元素跟一个特定的标签或者字符串绑定起来.
我们主要学习以下三个映射函数:1.replace(): 替换元素 2. rename(): 替换索引 3.map(): 新建一列.
替换元素(replace)
组装完数据结构后,里面通常会有些元素不符合需求. 需要将某些文本替换为另外的文本,例如存在外语文本.
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame1 = DataFrame({
'name': ['xxx', 'John', 'Edward', 'Smith', 'xxx', 'yyy', np.nan],
'age': np.arange(10, 17),
})
data_frame1
# 将DataFrame中的xxx替换为Obama, yyy替换成Polly
replace_map = {'xxx': 'Obama', 'yyy': 'Polly'}
data_frame1.replace(replace_map, inplace=True)
data_frame1
# 将DataFrame中的NaN替换成zzz
data_frame1.replace(np.nan, 'zzz')
重命名轴索引(rename)
pandas的rename函数可以用来替换轴的索引标签.
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame = DataFrame(np.arange(12).reshape((4, 3)))
data_frame
re_index = {
0: 'a',
1: 'b',
2: 'c',
3: 'd'
}
re_column = {
0: 'fir',
1: 'sec',
2: 'thi'
}
# 修改索引标签
# 也可以指定哪些标签需要修改,不需要每次都指定
data_frame.rename(index=re_index, columns=re_column, inplace=True)
data_frame
使用映射添加元素(map)
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame = DataFrame({
'name': ['John', 'Edward', 'Smith', 'Obama', 'Polly'],
'age': np.arange(18, 23),
})
data_frame
# 我们现在有一个字典存储了学生对应的学习成绩
score = {
'John': 89,
'Edward': 98,
'Smith': 85,
'Bush': '83',
'Obama': 78,
'Polly': 67,
'Donald': 77
}
# 现在我们想在data_frame中增加一列分数
data_frame['score'] = data_frame['name'].map(score)
data_frame
分组(GroupBy)
Hanley Wickham(许多R语言包的作者),创造了一个用于表示分组运算的术语'split-apply-combine'(拆分-应用-合并), 这个词很好描述了整个过程。 分组运算的第一个阶段, pandas对象(Series和DataFrame)中的数据会根据你所提供的一个或多个键被拆分(split)成多组. 拆分操作是在对象的特定轴上进行的. 例如,DataFrame可以在其行(axis=0)或列(axis=1)上进行分组. 然后, 讲一个函数应用(apply)到各个分组并产生一个新值. 最后,所有这些函数的执行结果会被合并(combine)到最终的结果对象中.
- 根据某一列或多列分组
from pandas import DataFrame, Series
import pandas as pd
import numpy as np
data_frame = DataFrame({
'key1': ['a', 'a', 'b', 'b', 'a'],
'key2': ['one', 'two', 'one', 'two', 'three'],
'data1': np.arange(5),
'data2': np.arange(10, 15)
})
data_frame
# 按照key1对数据进行分组
data_gruop_by_key1 = data_frame.groupby('key1')
# 输出a、b两组数据
dict([group for group in data_gruop_by_key1])['a']
dict([group for group in data_gruop_by_key1])['b']
# 计算每一组的平均值
print(data_gruop_by_key1.mean())
print('-'*30)
# 每一组元素个数
print(data_gruop_by_key1.size())
print('-'*30)
data1 data2
key1
a 1.666667 11.666667
b 2.500000 12.500000
------------------------------
key1
a 3
b 2
dtype: int64
------------------------------
# 对某一列数据进行分组
data1_gruop_by_key1 = data_frame['data1'].groupby(data_frame['key1'])
print(data1_gruop_by_key1.mean())
key1
a 1.666667
b 2.500000
Name: data1, dtype: float64
data_frame = DataFrame({
'key1': ['a', 'a', 'b', 'b', 'a'],
'key2': ['one', 'one', 'one', 'two', 'three'],
'data1': np.arange(5),
'data2': np.arange(10, 15)
})
data_frame
# 按照key1、key2进行分组
data_gruop_by_key12 = data_frame.groupby(['key1', 'key2'])
data_gruop_by_key12.sum()
- 根据索引值分组
data_frame = DataFrame(
np.random.randint(10, 13, (6, 6)),
index=list('aabbcc'),
columns=list('112233')
)
data_frame
# 根据列索引分组, 并求每一组平均值
data_frame.groupby(level=0, axis=1).mean()
# 根据行索引分组, 并求每一组平均值
data_frame.groupby(level=0, axis=0).mean()
- 手动指定索引进行分组
data_frame = DataFrame(
np.arange(36).reshape((6, 6)),
columns=list('abcdef')
)
data_frame
# 按照列进行分组
# 也就是手动指定哪些索引为一组
group_mapping = {
'a': 'first',
'b': 'first',
'c': 'first',
'd': 'second',
'e': 'third',
'f': 'third',
}
data_group_by_dict = data_frame.groupby(group_mapping, axis=1)
# 输出三组信息
print(dict([x for x in data_group_by_dict])['first'])
print('-'*13)
print(dict([x for x in data_group_by_dict])['second'])
print('-'*13)
print(dict([x for x in data_group_by_dict])['third'])
a b c
0 0 1 2
1 6 7 8
2 12 13 14
3 18 19 20
4 24 25 26
5 30 31 32
-------------
d
0 3
1 9
2 15
3 21
4 27
5 33
-------------
e f
0 4 5
1 10 11
2 16 17
3 22 23
4 28 29
5 34 35
# 对每一组进行求和计算
print(data_group_by_dict.sum())
first second third
0 3 3 9
1 21 9 21
2 39 15 33
3 57 21 45
4 75 27 57
5 93 33 69
# 将行索引分组
data_frame = DataFrame(
np.arange(36).reshape((6, 6)),
index=list('abcdef')
)
print(data_frame)
0 1 2 3 4 5
a 0 1 2 3 4 5
b 6 7 8 9 10 11
c 12 13 14 15 16 17
d 18 19 20 21 22 23
e 24 25 26 27 28 29
f 30 31 32 33 34 35
group_mapping = {
'a': 'first',
'b': 'first',
'c': 'first',
'd': 'second',
'e': 'third',
'f': 'third',
}
data_group_by_dict = data_frame.groupby(group_mapping, axis=0)
# 输出三组信息
print(dict([x for x in data_group_by_dict])['first'])
print('-'*25)
print(dict([x for x in data_group_by_dict])['second'])
print('-'*25)
print(dict([x for x in data_group_by_dict])['third'])
data_group_by_dict.sum()
0 1 2 3 4 5
a 0 1 2 3 4 5
b 6 7 8 9 10 11
c 12 13 14 15 16 17
-------------------------
0 1 2 3 4 5
d 18 19 20 21 22 23
-------------------------
0 1 2 3 4 5
e 24 25 26 27 28 29
f 30 31 32 33 34 35
pandas的I/O操作
我们学习了pandas库以及它所提供的用于数据分析的基础功能,了解了DataFrame和Series是这个库的核心,数据处理、计算和分析都是围绕它们展开.
Pandas是数据分析专用库,它所关注的是数据计算和处理。从外部文件读写数据也是数据处理的重要部分,于是pandas提供了用于文件数据读写的专门的工具, 这些工具提供了将不同的数据结构写入不同格式文件的方法,而无需过多考虑所使用的技术.
这些用于读写数据文件的函数分为对称的两大类:读取函数和写入函数.