《python for data analysis》第五章,pandas的基本使用

时间:2021-04-16 23:37:36

《利用python进行数据分析》一书的第五章源码与读书笔记

直接上代码

# -*- coding:utf-8 -*-
# 《python for data analysis》第五章, pandas基础
# 高级数据结构与操作工具 import pandas as pd
import numpy as np
import time start = time.time()
# pandas的数据结构, series and dataframe
# 1、series,类似一维数据, 一个字典,建立了从索引值(index)到数据值(values)的映射
# 组成:一组数据(numpy的各种数据类型,称为values)+数据标签(索引,称为index)+名称(称为name,values和index均有name属性,该属性可空缺)
# index缺省值为0~N-1的整数型索引,N为数据个数
# 创建一个series
np.random.seed(10)
series1 = pd.Series(np.arange(1, 6) + np.random.random(5))
print(series1) # 打印series
print('')
print(series1.values) # 打印series的values属性
print(series1.index) # 打印series的index属性
# 修改该series的index属性,也可在一开始创建series的时候就指定index,
# 即series1 = pd.Series(np.random.random(5), index=['a','aa','b','bb','z'])
# 也可通过字典创建series实现相同效果
# dict = {'a':1, 'aa':2, 'b':3, 'bb':4, 'z':5}
# series1 = pd.Series(dict)
series1.index = ['a', 'aa', 'b', 'bb', 'z'] # 三种方式效果一致
print('')
print(series1)
print('')
print(series1['aa']) # 可通过index索引的方式选择series中的值
print(series1[['a', 'aa']]) # 可一次性选取一组值,注意双括号
print('')
dict = {'a': 1, 'aa': 2, 'b': 3, 'bb': 4, 'z': 5}
series2 = pd.Series(dict, index=['a', 'aa', 'aaa'])
print(series2) # 'a'与'aa'在dict中均有对应值,直接写入到series中,'aaa'无对应值,故用NaN表示(NaN——缺失值或NA值)
# 对于多个series的运算,series在运算过程中会自动对齐,即同一个index的数据进行运算,不是所有series都有的index的结果用NaN表示
print('')
print(series1 + series2)
# series对象的values属性和index属性均有一个name属性,上面的series1和series2的name均缺省,可对该属性进行赋值
series1.name = 'distance' # values的name属性
series1.index.name = 'city' # index的name属性
print('')
print(series1)
# 2、dataframe,表格型数据结构,是一组有序的列
# dataframe的每个列可视为一个series,所有列共用一个行索引(index),每个series的name就是列索引(columns)
# index和columns可通过转置进行交换
# 创建一个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 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'], index=['one', 'two', 'three', 'four', 'five'])
# 对于找不到的列(如'debt'),dataframe中以NaN表示该列元素,行不可以多
print('')
print(dataframe)
# 赋值(广义的广播)
dataframe['debt'] = 10.2
# 通过标记方式(dataframe[])或属性方式(dataframe.)可将dataframe的某个列转换为一个series, 返回的series和dataframe有相同的行索引(index)
print('')
series3 = dataframe.year # 等价于 series3 = dataframe['year']
print(series3)
# dataframe通过索引返回的series是原dataframe的引用,而非复制,使用.copy()方法返回的就是原数据的复制
# series3[0]=20
# print(dataframe)
del dataframe['debt'] # 删除dataframe的某一列,只能按照列索引删除某列
print(dataframe)
dataframe = dataframe.T # dataframe可以进行转置,即行索引与列索引进行互换,dataframe.T不改变原dataframe,需要赋值操作
print(dataframe)
# 上述的dataframe是从字典(非嵌套),配以指定行索引,得到的
# 通过对嵌套字典进行dataframe()操作可以得到带有列索引与行索引的dataframe(无需指定行索引index)
# 2.1、二重嵌套字典,默认以外层字典的键为列索引columns,内层字典的键为行索引index
dict = {
'cityA': {2001: 2.1, 2002: 2.5},
'cityB': {2000: 1.5, 2001: 2.9, 2002: 3.0}
}
dataframe2 = pd.DataFrame(dict)
print(dataframe2)
# 2.2、三重及以上的嵌套字典,第一层字典的键为列索引columns,第二层字典的键为index,更内层的字典保留字典形式作为dataframe的value
dict = {
'cityA': {2001: {'pop': 1.2, 'debt': 2.1}, 2002: {'pop': 1.5, 'debt': 2.5}},
'cityB': {2001: {'pop': 1.8, 'debt': 2.9}, 2002: {'pop': 1.9, 'debt': 3.0}}
}
dataframe3 = pd.DataFrame(dict)
print(dataframe3)
print('')
# 3、索引对象
print(series1) # 选一个之前用过的Series
print(series1.index) # 打印该Series的索引对象,包括轴标签(a\aa\b\bb\z)、轴名称、dtype等
print('')
print(dataframe)
print(dataframe.index)
print(dataframe.columns)
print('')
print(series1.index)
print(series2.index)
print(series1.index.append(series2.index))
# index有很多方法,如.append可以把两个index接到一起。append还可以拼接Series、dataframe等
print('↑----------class1----------') # pandas的基本功能
# 1、重新索引, reindex
# 1.1、series的reindex
series1 = pd.Series(range(5), index=['d', 'a', 'c', 'b', 'e'])
print(series1)
series11 = series1.reindex(['a', 'b', 'c', 'd', 'e', 'blank'])
print(series11) # 行索引按照a、b、c、d、e、blank的顺序重新排列,其中blank在原series中无value,故以NaN计入,
series11 = series1.reindex(['a', 'b', 'c', 'd', 'e', 'blank'], fill_value=0) # 用0填充NaN值
print(series11)
# 对于单调(monotonic)变化的index(对原index而言),还可使用插值方法来对NaN值进行填充
series1 = pd.Series(np.random.randn(3), index=[1, 3, 5])
series11 = series1.reindex(range(1, 7), method='bfill') # 取后一个值填充NaN
series111 = series1.reindex(range(1, 7), method='ffill') # 取前一个值填充NaN
print(series11)
print(series111)
# 1.2、dataframe的reindex
# 创建一个dataframe
dataframe1 = pd.DataFrame(np.arange(1, 10).reshape(3, 3), index=['one', 'three', 'two'], columns=['b', 'a', 'c'])
print(dataframe1)
# 只传入一个index则默认是行index的修改,对于原dataframe中没有的值以NaN写入
dataframe11 = dataframe1.reindex(['one', 'two', 'three', 'four'])
# 上一行等价于dataframe11 = dataframe1.reindex(index = ['one','two','three','four'])
print(dataframe11)
# 修改列columns需要显式指定
dataframe11 = dataframe1.reindex(columns=['a', 'b', 'c'])
print(dataframe11)
# 也可同时修改行index和列columns
dataframe11 = dataframe1.reindex(index=['one', 'two', 'three'], columns=['a', 'b', 'c'])
print(dataframe11)
print('\n')
# dataframe的reindex也可以指定缺失值的填充值、插值方式等
# 2、丢弃指定轴(或该轴上的一些项)
# 2.1、series的drop,直接向drop()函数中送入要删的元素的index
series1 = pd.Series(range(3), index=['a', 'b', 'c'])
print(series1)
series2 = series1.drop('c')
print(series2)
# 2.2、dataframe的drop,送入的索引值需要指定轴号,0表示行索引,index;1表示列索引,columns;缺省值为0
dataframe1 = dataframe1.reindex(index=['one', 'two', 'three'], columns=['a', 'b', 'c'])
print(dataframe1)
dataframe2 = dataframe1.drop('three') # 等价于dataframe1.drop('three', axis = 0)
print(dataframe2)
dataframe2 = dataframe1.drop('c', axis=1) # 删除columns中某项需要显示指定第二条轴
print(dataframe2)
print('\n')
# 3、series/dataframe中values的索引、选取与过滤
# 3.1、series的索引、选取与过滤
print(series1)
print(series1[1]) # 类似numpy的array的索引,0表示第一个元素,类推得
print(series1[['a', 'b']]) # 用索引的标签值进行索引
print(series1['a':'b']) # 用索引的标签值进行切片是闭区间,而类似numpy的array索引方式是左闭右开区间
print(series1[0:1])
print(series1[series1 < 1]) # 布尔型索引
# 3.2、dataframe的索引、选取与过滤
# 3.2.1 输入dataframe的列名进行选列
print(dataframe1)
print(dataframe1['a']) # 该方式(输入列名columns)只能选列,等价于print(dataframe1.a)
# 3.2.2 dataframe切片选行
print(dataframe1[1:2]) # 选出第2行,即左闭右开,注意!!该方式只能切片索引,不能直接输入行号或者行的位置进行索引
# 3.2.3 dataframe布尔型数组选行!行
print(dataframe1[dataframe1['a'] > 2]) # 选出‘a'这一列中元素大于2的行
# 3.2.4 布尔型dataframe选元素
print(dataframe1[dataframe1 > 3]) # 打印dataframe1中大于3的元素,打印时整张表格会输出,小于等于3的元素以NaN表示
# dataframe直接索引的方式就这几种,可以看到无法通过行标签(one、two、three)进行索引
# 为解决该问题,引入.ix字段,可以直接根据行标签进行选行,注意!直接输入列标签选列是不行的,毕竟已经有3.2.1了
print(dataframe1.ix['one'])
# .ix[]同时输入行标签、列标签进行子表的选取
print(dataframe1.ix[['one', 'two'], ['a', 'b']]) # 注意顺序!!!先给出行标签再给出列标签
print(dataframe1.ix[2]) # 还可以给出行的位置序号选行
print('\n')
# 特别对于整数索引的情况,上面这句话就会有歧义,目前规则是.ix面向标签,.irow和.icol面向位置,本章最后进行介绍
# 4、算术运算与数据对齐
# 两个不同索引的对象(Series or Dataframe)进行算术运算(直接的+-*/号),结果的索引为两者的并集,但仅交集有数据,其余为NaN
dataframe1 = pd.DataFrame(np.arange(1, 10).reshape(3, 3), index=['one', 'two', 'three'], columns=['a', 'b', 'c'])
dataframe2 = pd.DataFrame(np.arange(-10, -1).reshape(3, 3), index=['one', 'next', 'near'], columns=['a', 'd', 'e'])
print(dataframe1)
print(dataframe2)
print(dataframe1 + dataframe2)
# 用add、sub、div、mul来替代+、-、*、/,这样可以指定一个填充值来替代缺失值
print(dataframe1.add(dataframe2, fill_value=0)) # 某对标签若在两个对象中均无数值则仍未NaN
# Dataframe与Series之间的运算采用广播机制
# 行上广播直接采用+-*/即可
series1 = dataframe1.ix['one']
print(dataframe1)
print(series1)
print(dataframe1 - series1)
# 若在列上广播则需要用add、sub、mul、div,并指定轴为0!注意,是显示指定,不能缺省。
series1 = dataframe1['a']
print(dataframe1)
print(series1)
print(dataframe1 - series1) # 全为NaN,原因同上,无交集
print(dataframe1.sub(series1, axis=0))
print('\n')
# 5、函数应用与映射
# numpy有一些元素级数组方法,比如abs,可直接用于pandas对象
print(dataframe2)
print(dataframe2.abs())
# 也可以自定义一些函数,通过.apply方法应用于pandas对象的行或者列(通过axis=0 or 1进行指定)
function = lambda x: x.min() - x.max()
print(dataframe1)
print(dataframe1.apply(function, axis=0))
print(dataframe1.apply(function, axis=1))
# 也可以自定义一些元素级函数,通过.applymap()方法应用于dataframe对象,通过.map()方法应用于series对象
function = lambda x: x / 2
print(dataframe1.applymap(function))
# 6、排序与排名
# 6.1、list和array的排序,对前一章的回顾
np.random.seed(10)
list = list(np.random.random(5)) # array方法相同
print(list)
list.sort() # 也可以用list = np.sort(list)
print(list)
print('\n')
# 6.2 pandas对象的排序,通过.sort_index()方法进行实现,按索引进行排序
# 默认均为升序,降序需要显式指定ascending=False
series = pd.Series(np.random.random(5), index=['d', 'b', 'c', 'e', 'a'])
print(series)
print(series.sort_index()) # series不需要指定轴号
dataframe = pd.DataFrame(np.arange(9).reshape(3, 3), columns=['b', 'c', 'a'], index=[3, 1, 2])
print(dataframe)
print(dataframe.sort_index()) # 缺省axis=0
print(dataframe.sort_index(axis=1, ascending=False))
print('\n')
# 若要按照value进行排序,则series用np.sort进行,索引会被抹去,dataframe还是用sort_index,并加入参数by指定排序的列
print(series)
print(np.sort(series))
print(dataframe)
print(dataframe.sort_index(by='a', ascending=0)) # 该方法只能进行列上的排序
# by传入一个list时进行多列上的排序,list中越靠前的列排序优先级越高
print('\n')
# 6.3、排名,即在排序的基础上增设一个从0到数据量的排名值
# 该排名值可按照一定关系改变平级关系
series = pd.Series([2, 1, 2, -1, -2])
print(series.rank()) # 默认给出平均排名,如2,3并列则都给出2.5
print(series.rank(method='first')) # 先出现者排名高
print(series.rank(method='min')) # 统一给出小排名,如2,3并列都给2
print(series.rank(method='max')) # 统一给出大排名,如2,3并列都给3
print('\n\n') # 7、重复值的轴索引
# pandas对象允许轴索引重复,不过实际使用中最好还是避免重复,很多pandas函数是要求index不重复的
# Series为例,dataframe就是行索引
series = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
print(series)
print(series.index.is_unique) # pandas对象的index对象有一个is_unique属性,用于判断索引唯一与否
print(series['a']) # 会打印出所有该轴标签的value
print('↑----------class2----------\n\n') # 汇总与计算描述统计
# pandas有很多数学统计方法,如.sum()、.cumsum()、.mean()等,默认对每一列进行运算
# 相较numpy中的同功能函数,numpy不允许存在数据缺失而pandas允许,并可以对缺失值进行一些基本的处理
dataframe = pd.DataFrame([[1, 2, np.nan], [4, 5, 6], [7, 8, 9]], columns=['a', 'b', 'c'], index=['one', 'two', 'three'])
print(dataframe)
print(dataframe.sum(axis=1, skipna=True)) # 每行求和,并跳过缺失值,axis缺省0,skipna缺省True
print(dataframe.sum(axis=1, skipna=0)) # 不调过NaN,则所有涉及到NaN的运算结果均为NaN,同numpy的原则
ar1 = np.array([1, np.nan, np.nan, np.nan, 5])
print(ar1)
print(ar1.sum())
# dataframe的describe方法可返回一个表的汇总统计结果
print(dataframe1)
print(dataframe1.describe())
print('') # 相关系数与协方差的计算
# 补充数据拼接
series1 = pd.Series(range(5), index=['day1', 'day2', 'day3', 'day4', 'day5'], name='companyA')
series2 = pd.Series(sorted(range(5), reverse=True), index=['day1', 'day2', 'day3', 'day4', 'day5'], name='companyB')
print(series1)
print(series2)
dataframe = pd.concat([series1, series2], axis=1)
print(dataframe)
# 用.cov()方法计算协方差,用.corr()方法计算相关系数。要求:重叠、非NA、按索引对齐
# series之间的计算cov和corr
print(dataframe.companyA.cov(dataframe.companyB))
print(dataframe.companyA.corr(dataframe.companyB))
print('')
# 单个dataframe中计算cov和corr
print(dataframe.cov())
print(dataframe.corr())
print('')
# dataframe与series之间,dataframe与dataframe之间,用.corrwith()方法计算相关系数
print(dataframe.corrwith(dataframe.companyA)) # 传入的series与dataframe的各列计算相关系数
print(dataframe.corrwith(dataframe)) # 两个dataframe的列按列名进行相关系数的计算
print('')
# 唯一值、值计数及成员资格
# 从series中抽取信息
# 1、.unique()方法,得到唯一值数组,功能类似set()
series = pd.Series([9, 10, 1, 1, 1, 2, 3, 2, 3, 4, 5, 6, 7, 8])
print(series.unique()) # 结果是未排序的
# 2、.value_counts()方法,计算series中各值出现的次数
print(series.value_counts())
# 补充,list相应有.count()方法计算元素出现次数
list1 = [1, 2, 2, 3, 3, 3]
print(list1.count(1))
print(list1.count(2))
# 3、.isin()方法可用于判断series中各值是否位于另一个矢量化集合中
print(series.isin([1, 2, 3]))
print('↑----------class3----------\n\n') # 处理缺失数据
# pandas中,python内置的None和numpy的np.nan都识别为NaN
# 1、滤除缺失数据
# 1.1、对于series,直接通过.dropna()方法丢弃缺失值及其索引
series = pd.Series([1, 2, None, None, 3, 4, 5])
print(series)
print(series.dropna())
# 1.2、对于dataframe,dropna()缺失为丢弃所有含有NaN的行
dataframe = pd.DataFrame(np.arange(1, 10).reshape(3, 3), columns=['a', 'b', 'c'], index=['one', 'two', 'three'])
dataframe.ix[1:, 1] = None
dataframe.ix[1, :] = None
print(dataframe)
print(dataframe.dropna()) # 有NaN就丢
print(dataframe.dropna(how="all")) # 全为NaN就丢
print(dataframe.dropna(axis=1)) # 指定列上操作
print(dataframe.dropna(thresh=2)) # 给定丢弃阈值,每行有2个及以上的非NaN值时保留该行
# 2、填充缺失数据
print(dataframe)
print(dataframe.fillna(0)) # 直接用常数填充,可传入一个函数值,如dataframe.fillna(dataframe.mean()),每列平均值
print(dataframe.fillna({'a': -1, 'b': -2})) # 传入字典,按照列名进行填充,没有的则保持为NaN
print(dataframe.fillna(method='ffill')) # 插值方法仍然有效,ffill和bfill
print('↑----------calss4-----------') # 层次化索引,用低维数据形式表示高维数据
# 1、层次化索引的series,相当于一张dataframe
series = pd.Series(np.arange(9), index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c'], [1, 2, 3, 1, 2, 3, 1, 2, 3]])
print(series) # 该series的索引是层次化的(两层)
print(series['a']) # 外层索引选取的数据还是series
print(series['a', 2]) # 双层索引
# 通过.stack()和.unstack()可以实现层次化索引的series和dataframe之间的转换
dataframe = series.unstack()
print(dataframe)
print(dataframe.stack()) # 又变回层次化索引的series
# 2、层次化索引的dataframe,表示更高维数据,其行索引与列索引均可以层次化
dataframe = pd.DataFrame(np.arange(12).reshape(-1, 3),
columns=[['countrya', 'countrya', 'countryb'], ['citya', 'cityb', 'cityc']],
index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]])
print(dataframe)
print(dataframe['countrya']['citya'].ix['a'][1]) # 先选列再选行
print(dataframe.stack().stack()) # 一次.stack()把一层列索引打包到行索引上
# 3、通过.swaplevel()可以交换层次化轴索引的顺序,传入轴索引name
dataframe.columns.names = ['country', 'city']
dataframe.index.names = ['one', 'two']
print(dataframe)
print(dataframe.swaplevel('city', 'country', axis=1))
print('')
# 4、通过.sortlevel()可以对某一个级别索引进行排序
print(dataframe)
print(dataframe.swaplevel().sortlevel(level=0, axis=1, ascending=False)) # 对列索引的第一级索引进行排序
print('\n')
# 5、对于层次化索引的pandas对象,之前提到的统计函数如sum、mean等均可指定level进行计算
print(dataframe)
print(dataframe.mean(axis=1, level=0)) # 对每一行在第一层索引上进行取平均
# 6、用dataframe的列来当作行索引
# 之前用.stack()和.unstack()方法进行了行索引与列索引之间的转换
# 还可以用.set_index()和.reset_index()进行列的value与行索引标签之间的转换,相应地,列索引标签与行索引name进行转换
dataframe = pd.DataFrame(np.arange(25).reshape(5, 5), columns=['a', 'b', 'c', 'd', 'e'])
print(dataframe)
print(dataframe.set_index(['a', 'b']))
print(dataframe.set_index(['a', 'b']).reset_index(['a', 'b']))
print('↑----------class5---------\n\n') # pandas的其他话题 —— 整数索引 与 panel(面板)数据
# 1、整数索引,有时候会有意外情况出现
dataframe = pd.DataFrame(np.arange(9).reshape(3, 3), index=[-1, 0, 1], columns=['a', 'b', 'c'])
print(dataframe)
print(dataframe.ix[-1]) # 或许会有疑问,这里的-1是指索引为-1呢还是位置为-1呢?
# 在pandas中,直接索引([])和.ix()方法索引,给出的值都是按照标签去进行索引
# 用.irow()和.icol()方法则是按照位置去进行索引(dataframe),发现高版本pandas中已经没有.irow().icol()方法了
# 这一个曾经有过教训
# 事情是这样的,有一个很多行(比如1000行)的csv,我用read_csv(‘’,chunksize=100)分块读取
# 想用抽样的方法对其减小体积,取100个中的前5个保存到新的csv文件中
# 写为
# slices = pd.read_csv('filename.csv',chunksize=100),此时索引为缺省的整数索引
# for slice in slices:
# slice.ix[:5,:].to_csv('newfilename.csv',method='a') , 追加写入方式
# 本来应该要看到50行,但是打开csv只看到了5行数据
# 这是因为.ix[]进行索引是按照标签进行的,:5是选择了索引为0、1、2、3、4这5行,之后的切片中都没有索引为0~4的了,所以
# 追加写入的都是空集
# 应该修改为
# for slice in slices:
# slice[:5].to_csv('newfilename.csv',method='a')
# 切片选行是基于位置进行索引的 # 2、panel,面板数据类型,三维版的dataframe
# 一个panel由很多张dataframe组成,panel和层次化索引的dataframe之间可以通过.to_dataframe()和.to_panel()方法进行转换
# 前提——dataframe的层次化索引是行索引
dataframe = pd.DataFrame(np.arange(12).reshape(-1, 3),
columns=['countrya', 'countryb', 'countryc'],
index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]])
panel = dataframe.to_panel()
print(dataframe)
print(panel)
print(panel.ix['countrya', 'a', :]) # 索引轴依次是item、major、minor。可切片。 print('\n')
print("-------------that's all------------------")
print('Total time is %.5f s' % (time.time() - start))