本节主要记录 Python 中 Pandas 库的学习过程,以生信修炼手册和官方文档为基础。
初识 pandas
在 pandas 中,提供了以下两种基本的数据结构 Series 和 DataFrame,Series 相当于 R 中的向量,DataFrame 相当于数据框。
pandas 中的缺失值用 NaN 来表示。
import pandas as pd
s = pd.Series([1, 2, 3, np.nan, 5])
s = pd.Series(data=[1, 2, 3, np.nan, 5],name='A') # name指定Series名称
s = pd.Series(data=[1, 2, 3, np.nan, 5],index=['A','B','C', 'D', 'E']) # data参数指定数据,index参数指定标签
df = pd.DataFrame(np.random.rand(5,5))
# index 参数指定行标签, 默认值为从0开始的下标
# columns参数指定列标签,默认值为从0开始的下标
df = pd.DataFrame(np.random.rand(5,5),index=['A1','A2','A3','A4','A5'], columns=['A','B','C','D','E'])
df['A'] df.A df.get('A')
# 属性
df.ndim # 维度
df.size # 数值总数
df.shape # 每个维度大小
df.dtypes # 每一列的数据类型
df.values # 数据框的所有值
df.axes # 行列标签
df.index # 行标签
df.keys() # 行标签
df.columns # 列标签
# 查看内容
df.head(n=2)
df.tail(n=2)
# 访问元素
df.at["A1","A"] # 根据行和列的标签来访问对应元素
df.loc['A1','A'] # 同上
df.iat[0,0] # 根据行和列的索引来访问对应元素
df.iloc[0,0] # 同上
# 运算
a.add(b)
a + b
a.sub(b)
a.mul(b) # 乘法
a.div(b) # 除法
a.gt(b) # 大于
a.lt(b) # 小于
a.le(b) # 小于等于
a.ge(b) # 大于等于
a.eq(b) # 等于
a.ne(b) # 不等于
使用 Pandas 进行文件读写
# CSV文件读写
import pandas as pd
a = pd.read_csv("test.csv")
# 常见参数
pd.read_csv("test.csv",sep="\t")
pd.read_csv("test.csv",delimiter = "\t")
pd.read_csv('test.csv', comment = "#") # 指定注释行标识
pd.read_csv('test.csv', header = 0) # 第一行为表头
pd.read_csv('test.csv', header = None) # 无表头
pd.read_csv('test.csv', index_col=0) # 指定对应列为行标签
pd.read_csv('test.csv', usecols = (0, 1)) # 读取特定的列
pd.read_csv('test.csv', header = None, skiprows = 1) # 跳过几行
pd.read_csv('test.csv', nrows = 2) # 仅仅读取前几行内容
pd.read_csv('test.csv', na_values = 3) # 指定空值的形式
a.to_csv("test.csv",header = None,index = False) # 输出到csv文件
# Excel文件读写
pd.read_excel("test.xlsx",sheet_name = 0)
pd.read_excel("test.xlsx",sheet_name = "Sheet3")
df.to_excel("output.xlsx",sheet_name = 'Sheet1')
访问和提取 DataFrame 中的元素
# 构建数据框
df = pd.DataFrame(np.random.randn(4, 4), index=['r1', 'r2', 'r3', 'r4'], columns=['A', 'B', 'C', 'D'])
## 属性运算符
# 属性操作符的本质是先根据列标签得到对应的Series对象,再根据Series对象的标签来访问其中的元素
s = df.A
s.r1
df.A.r1
## 索引运算符
## 对列进行操作,用列标签来访问对应的列
df["A"]
df["A"]["r1"]
df["A"][0]
df["A","B"]
df['E'] = 5 # 对不存在的列标签设值时,会自动进行append操作
# 对行进行切片的操作
df[:2]
# loc 提供基于标签的访问方式
df.loc['r1','A']
df.loc['r1':'r3', 'A':'C'] # 支持切片
# iloc 提供基于下标索引访问元素的方式
df.iloc[0] # 行
df.iloc[0, 0] # 先行后列
df.iloc[[0, 1], [0, 1, 2]]
df.iloc[:2, :2]
# at系列函数
# at使用标签进行访问,iat使用位置索引进行访问
df.at['r1', 'A']
df.iat[0, 0]
pandas 中多种合并连接数据框方法
# 第一种 concat
import numpy as np
import pandas as pd
a = pd.DataFrame(np.random.randn(2,2),columns=['A','B'])
b = pd.DataFrame(np.random.randn(3,3),columns=['A','B', 'C'])
pd.concat([a,b])
# 默认以行的方式进行合并,对于没有的列使用NaN进行填充
# axis参数
# 行为0轴,列为1轴
pd.concat([a,b], axis = 0)
pd.concat([a,b], axis = 1)
# join参数
# 合并数据框时,沿着axis参数指定的轴进行合并,而join参数则控制在另外一个轴上,标签如何处理,默认的outer表示取并集,取值为inner时,取交集,只保留overlap的标签。
pd.concat([a, b], join = "outer") # join针对列,并集
pd.concat([a, b], join = "inner") # join针对列,交集
# ignore_index参数
# 忽略在axis参数指定的轴上的原有标签
# False为保留原有的标签,True则相反
pd.concat([a, b], ignore_index = False)
pd.concat([a, b], ignore_index = True)
# key参数
# 用于指定一个新的index
pd.concat([a, b], keys = ['a', 'b'])
# names参数为index设置名称
pd.concat([a, b], keys = ['a', 'b'], names = ['groupA', 'groupB'])
# verify_integrity参数
# 设置是否允许axis参数指定的轴上有重复的标签
# False为允许
pd.concat([a, b], verify_integrity=False))
# 第二种 merge
a = pd.DataFrame({'name':['Rose', 'Andy', 'July'],'age':[21,22,18]})
b = pd.DataFrame({'name':['Rose', 'Andy', 'Jack'],'height':[172,168,175],'weight':[45,55,75]})
# 默认情况下,会寻找标签名字相同的列作为key, 然后比较两个数据框中key列对应的元素,取交集的元素作为合并的对象。
a.merge(b)
# on参数
a.merge(b, on = "name")
# how参数
a.merge(b,how = "inner") # 取交集
a.merge(b,how = "outer") # 取并集
a.merge(b,how = "left") # 只取第一个数据框的元素
a.merge(b,how = "right") # 只取第二个数据框的元素
# left_on/right_on参数
# 当两个数据框中没有overlap的标签名时使用
a.merge(b, left_on='student_name', right_on='name')
# suffixes参数
# 自定义后缀
a.merge(b, on = 'name', suffixes = ['_a', '_b'])
a.merge(b, left_index = True, right_index = True) # 将行标签作为key
# 第三种 join
# join的合并方式和merge相同, 默认根据行标签进行合并, 优势在于可以一次处理多个数据框
a = pd.DataFrame(np.random.randn(2,2),columns=['A','B'])
b = pd.DataFrame(np.random.randn(3,3),columns=['A','B','C'])
a.join(b, lsuffix='_a', rsuffix='_b')
# 第四种 append
# append将两个数据框以行的方式进行合并,要求列数相同
a = pd.DataFrame(np.random.rand(2, 2), columns=['A', 'B'])
b = pd.DataFrame(np.random.rand(2, 2), columns=['A', 'B'])
a.append(b)
# 第五种 assign
# 用于给数据框新增列
a.assign(C=pd.Series([1,2]))
a.assign(C=pd.Series([1,2]),D=[1,2])
pandas 中数据框的 reshape 操作
pandas 中的 reshape 操作与 R 语言中的 reshape 操作类似。
# 第一种 stack
import pandas as pd
import numpy as np
a = pd.DataFrame(np.random.rand(4, 2),index=['G1', 'G2', 'G3', 'G4'], columns=['A', 'B'])
a.stack() # 将所有列标签转换为行标签
## 列标签为multiindex情况时,通过level和dropna参数控制
multi_index = pd.MultiIndex.from_tuples([('groupA', 'A'),('groupB', 'B')])
a = pd.DataFrame(np.random.rand(4, 2),index=['G1', 'G2', 'G3', 'G4'], columns=multi_index)
a.stack() # 默认最后一个列标签
a.stack(level=1)
a.stack(level=0)
a.stack(level=[0,1]) # 指定多个标签
# dropna参数用于去除NaN的值
# unstack
index = pd.MultiIndex.from_tuples([('G1','A',),('G1','B'), ('G2','A'),('G2','B')])
a = pd.Series(np.random.rand(4),index=index)
a.unstack()
# 通过level参数指定行标签的下标,默认值为-1
a.unstack(level=0)
a.unstack(level=1)
# melt
# 1. stack会将所有的列标签都进行转换,而melt函数则可以通过参数指定需要转换的列
# 2. 在于转换后的列标签不是以index的形式出现,而是作为数据框中的variable列
# 通过value_vars参数,指定需要进行转换的列
# 可以通过var_name和value_name参数进行自定义列名
# pviot
# pivot函数的作用和unstack类似,实现数据框由长到宽的转换。相比unstack函数,pivot更加灵活,通过指定index,columns, values3个参数指定需要对应的列
pandas 中的数据处理利器-groupby
将数据拆分处理后再合并
groupby 的操作过程如下:
split, 第一步,根据某一个或者多个变量的组合,将输入数据分成多个 group
apply, 第二步, 对每个 group 对应的数据进行处理
combine, 第三步,将分组处理的结果合并起来,形成一个新的数据
import numpy as np
import pandas as pd
df = pd.DataFrame({'x':['a','a','b','b','c','c'],'y':[2,4,0,5,5,10]})
df
df.groupby('x').mean()
# groupby函数的返回值为为DataFrameGroupBy对象,存在一些基本的属性和方法
grouped = df.groupby('x')
# groups属性,返回值为字典,key是分组的类别
grouped.groups
# len函数可以获得分组后的组别数
len(grouped.groups)
# get_group方法可以获得每个group对应的数据框
grouped.get_group('a')
# 在groupby中分组可以依据单个标签也可以是多个标签的组合
# 分组处理
df.groupby('x').count()
df.groupby('x').size()
df.groupby('x').sum()
df.groupby('x').mean()
df.groupby('x').median()
df.groupby('x').var()
df.groupby('x').std()
df.groupby('x').min()
df.groupby('x').max()
# 通过aggregate方法则可以灵活的使用各种函数,其中apply更灵活
# 当需要根据某种条件对group进行过滤时,可以使用filter方法
df.groupby('x').filter(lambda x: x.mean() > 2.5)
# transform方法返回一个和输入的原始数据相同尺寸的数据框,常用于在原始数据框的基础上增加新的一列分组统计数据
pandas 中的 index 对象详解
对于 index 对象而言,主要存在两个类别:index 和 MultiIndex;MultiIndex 指的是多层索引,Index 是单层索引。
# 单层索引
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.rand(4, 4))
df.index
df.columns
# 默认的行列标签皆为一个RangeIndex对象
# 通过Index函数可以显示创建Index对象
df.index = pd.Index(list('ABCD'))
df.columns = pd.Index(list('abcd'))
# 同样存在一些基本属性
a = pd.Index(list('ABCD'))
a.array # 值构成数组
a.dtype # 数据类型
a.nlevels # level个数
a.ndim # 维度
a.shape # 尺寸
a.size # 个数
a.values # 值
# 多种类型索引
# NumericIndex
# 浮点数
pd.Float64Index([1, 2, 3, 4])
# 整数
pd.Int64Index([1, 2, 3, 4])
# 无符号的整数
pd.UInt64Index([1, 2, 3, 4])
# 等差序列
pd.RangeIndex(start=1,stop=8,step=2)
# CategoricalIndex
# 类似R语言中的因子,用于约束可选值的范围,超过范围的值强制变为NaN
a = pd.CategoricalIndex(list('ABCD'), categories=['A', 'B', 'C', 'D'])
a.codes
a.categories
a.ordered
# IntervalIndex
# 索引的值为一个区间
a = pd.interval_range(start=0, end=4)
a.left # 区间左侧的值
a.right # 区间右侧的值
a.length # 区间长度
a.mid # 区间中心点
# DatetimeIndex/PeriodIndex
# 索引的值为日期和时间,可以通过date_range和PeriodIndex函数生成
a = pd.date_range('2020-01-01', periods=4, freq='D')
a.year
# TimedeltaIndex
# 将时间间隔转换为时间戳
df.index = pd.TimedeltaIndex([12, 24, 36, 48], unit='h')
# 当多个单层的索引结合在一起时,就形成了MultiIndex
# 通过数组创建
arrays = [[1, 2, 3, 4], ['A', 'A', 'B', 'B']]
pd.MultiIndex.from_arrays(arrays, names=('index', 'group'))
df.index = pd.MultiIndex.from_arrays(arrays, names=('index', 'group'))
# 通过元组创建
pd.MultiIndex.from_tuples(([1, 'A'], [2, 'A'], [3, 'B'], [4, 'B']))
df.index = pd.MultiIndex.from_tuples(([1, 'A'], [2, 'A'], [3, 'B'], [4, 'B']))
# 通过数据框创建
index = pd.DataFrame({'index':[1, 2, 3, 4], 'group':['A', 'A', 'B', 'B']})
pd.MultiIndex.from_frame(index)
df.index = pd.MultiIndex.from_frame(index)
# from_product
# 适合元素的快速组合
pd.MultiIndex.from_product([[1,2],['A','B']])
df.index = pd.MultiIndex.from_product([[1,2],['A','B']])
pandas 中的字符串处理函数
Series 对象指的是一维数据框。
# 大小写转换
df[1] = df[0].str.lower()
df[2] = df[1].str.upper()
# 去除空白
# str.strip, 去除字符串前后两端的空白
df[0].str.strip().array
# str.lstrip, 去除字符串前端的空白
df[0].str.lstrip().array
# str.lstrip, 去除字符串后端的空白
df[0].str.rstrip().array
# 拆分
# 默认按照指定的分隔符进行拆分,结果为列表
df[0].str.split('_')
# n参数,指定分隔的次数
df[0].str.split('_', n = 1)
# 替换
df[0].str.replace('_', '-')
df[0].str.replace('[\d_]+', '') # 正则表达式
df[0].str.replace('_', '-', regex=False) # 不使用正则表达式
# 拼接
# 针对Series对象
# 单个Series对象,将所有数据拼接在一起
df[0].str.cat()
df[0].str.cat(sep=',')
# 当两个数组拼接时,返回一个新的Series对象
df[0].str.cat(['1','2', '3', '4'])
# 当拼接的对象为一个数据框时,将数据框的所有列都进行拼接
df[1] = df[0].str.cat(['1','2', '3', '4'])
# 判断是否包含子字符串
df[0].str.contains('1')
df[0].str.contains('\w+')
# 用str.match函数来实现从头开始的全局查找
# 提取子字符串
# 通过str.extract和str.extractall函数来实现
pandas 中的缺失值处理
# 当需要人为指定一个缺失值时,默认用None和np.nan来表示
# 都会被默认被识别为NaN
# 缺失值的判断
a.isna()
a.notna()
# 缺失值填充
# value参数,表示用一个指定的值来替换缺失值
a.fillna(value=1)
# method参数,指定一种方法来填充缺失值
# pad方法,表示用NaN前面一个值来进行填充
a.fillna(method = 'pad')
# bfill法,表示用NaN后面一个值来进行填充
a.fillna(method = 'bfill')
# 对每一列的NaN值,依次用对应的均值来填充
df.fillna(df.mean())
# 缺失值删除
# 通过dropna方法来快速删除NaN值
a.dropna()
# dropna操作数据框时,可以设置axis参数的值
# 默认为0,表示去除包含 了NaN的行
# axis=1,表示去除包含了NaN的列
df.dropna()
pandas 中窗口处理函数
通过 rolling 和 expanding 系列函数,可以按照窗口的方式来灵活处理序列。
pandas中的日期和时间操作
Date Time用于表示某个具体的时间点,Time spans用于生成时间间隔相同的时间序列;Time deltas表示时间间隔,Date offsets则表示日期间隔,这二者的作用都是用于时间运算,通过时间点+时间间隔的方式,得到新的时间点。
data times
# 单个时间戳通过Timestamp函数生成
pd.Timestamp('2020-07-01')
pd.Timestamp(2020,07,01)
# 多个时间戳通过to_datetime和date_range方法产生
pd.to_datetime(['2020-07-01', '2020-07-02', '2020-07-03'])
pd.date_range('2018-07-01', periods=3, freq='D') # 天间隔
pd.date_range('2018-07-01', periods=3, freq='W') # 周间隔
pd.date_range('2018-07-01', periods=3, freq='H') # 小时间隔
Time spans
# 用于生成时间间隔相同的时间序列
pd.Period('2020-07-01')
pd.PeriodIndex(['2020-07-01', '2020-07-02', '2020-07-03', '2020-07-04'], freq='D')
pd.period_range(start='2020-07-01', end='2020-07-04', freq='D')
pd.period_range(start='2020-07-01', freq='D', periods=4)
Time deltas
# 生成时间间隔
pd.Timedelta('1 day')
pd.to_timedelta('1day')
pd.timedelta_range(start='1 day', periods=4)
pd.timedelta_range(start='1 day', periods=4, closed='right')
pd.timedelta_range(start='1 day', periods=4, freq='12H')
Date offsets
# 与time deltas类似,但是是日期的间隔
pd.DateOffset(1) # 单位是day, 即1天
pd.Timestamp('2020-07-01') + pd.DateOffset(1) # 两者一致
pd.Timestamp('2020-07-01') + pd.Timedelta('1 day')