pandas_基础
import pandas as pd
文件读取和写入
文件读取
df_csv = pd.read_csv('.../.csv',header=None,index_col=['col1', 'col2'],usecols=['col1', 'col2'],parse_dates=['col5'],nrows=2)
df_txt = pd.read_table('.../.txt')
df_excel = pd.read_excel('../.xlsx')
参数说明:
header=None表示第一行不作为列名
index_col表示把某一列或几列作为索引(索引的内容将在后续博文中进行详述)
usecols表示读取列的集合,默认读取所有的列
parse_dates表示需要转化为时间的列(关于时间序列的有关内容将在后续博文讲解)
nrows表示读取的数据行数
注:在读取txt文件时,df_txt = pd.read_table(’…/ .txt’),会遇到分隔符非空格的情况,分割参数sep,使得用户可以自定义分割符号,进行txt数据的读取。
分隔符为||||,进行更改:
pd.read_table(’…/ .txt’, sep=’ |||| ', engine=‘python’) 指定引擎为python
sep使用正则表达式,因此需要对|进行转义变成 |
数据写入
把index设置为False(特别当索引没有特殊意义的时候)把索引在保存的时候去除。
df_csv.to_csv('../data/my_csv_saved.csv', index=False)
df_excel.to_excel('../data/my_excel_saved.xlsx', index=False)
pandas中没有定义to_table函数,但是to_csv可以保存为txt文件,并且允许自定义分隔符,常用制表符\t分割:
df_txt.to_csv('../data/my_txt_saved.txt', sep='\t', index=False)
把表格快速转换为markdown和latex语言,可以使用to_markdown和to_latex函数。
print(df_csv.to_markdown())
print(df_csv.to_latex())
数据结构
存储一维values的Series和存储二维values的DataFrame,在这两种结构上定义了很多的属性和方法。
Series
Series一般由四个部分组成: 序列的值data、索引index、存储类型dtype、序列的名字name;通过s.values、s.index、s.dtype、s.name、s.shape等 获取属性
s = pd.Series(data = [100, 'a', {'dic1':5}],index = pd.Index(['id1', 20, 'third'], name='my_idx'),dtype = 'object', name = 'my_name')
其中,object代表了一种混合类型,正如上面的例子中存储了整数、字符串以及Python的字典数据结构。此外,目前pandas把纯字符串序列也默认认为是一种object类型的序列,但它也可以用string类型存储
DataFrame
在Series的基础上增加了列索引,一个数据框可以由二维的data与行列索引来构造:
data = [[1, 'a', 1.2], [2, 'b', 2.2], [3, 'c', 3.2]]
df = pd.DataFrame(data = data,
index = ['row_%d'%i for i in range(3)],
columns=['col_0', 'col_1', 'col_2'])
采用从列索引名到数据的映射来构造数据框,同时再加上行索引:
df = pd.DataFrame(data = {'col_0': [1,2,3],
'col_1':list('abc'),
'col_2': [1.2, 2.2, 3.2]},
index = ['row_%d'%i for i in range(3)])
取出相应的列 或者 由多个列组成的表
df['col_0']
df[['col_0', 'col_1']]
通过df.values、df.index、df.dtype(返回的是值为相应列数据类型的Series)、df.name、df.shape等 获取属性;df.T 将DataFrame进行转置。
常用基本函数
df = pd.read_csv('../data/learn_pandas.csv')
print(df.columns)
df = df[df.columns[:7]] # 只使用前7列
pritn(df)
汇总函数
df.head(n) # 返回表或者序列的前n行,n默认为5
df.tail(n) # 返回表或者序列的后n行,n 默认为5
df.info() # 返回表的信息概况
df.describe() # 表中数值列对应的主要统计量
特征统计函数
df_demo = df[['Height', 'Weight']]
df_demo.mean() # 均值
df_demo.max() # 最大值
df_demo.quantile(0.75) # 分位数
df_demo.count() # 非缺失值个数
df_demo.idxmax()# 最大值对应的索引
df_demo.idxmin() # 最小值对应的索引
上述函数又称为聚合函数,都有参数axis,默认为0(逐列聚合),设置为1,逐行聚合。
唯一值函数
一列的唯一值
df['School'].unique() # 得到序列唯一值组成的列表
df['School'].nunique() # 得到序列唯一值的个数
df['School'].value_counts() # 得到唯一值和其对应出现的频数
df['School'].drop_duplicates()
df['School'].duplicated()
多个列组合的唯一值
df_demo = df[['Gender','Transfer','Name']]
df_demo.drop_duplicates(['Gender', 'Transfer'],keep='last')
关键参数keep:
默认值first表示每个组合保留第一次出现的所在行;
last表示保留最后一次出现的所在行;
False表示保留只出现过一次的组合。
df_demo.duplicated(['Gender', 'Transfer'])
返回是否为唯一值的布尔列表(重复元素为True,否则为False),keep参数与drop_duplicates相同。
drop_duplicates等价于把duplicated为True的对应行剔除。
替换函数
替换操作是针对某一个列进行的,因此下面的例子都以Series举例。pandas中的替换函数可以归纳为三类:映射替换、逻辑替换、数值替换。
映射替换(replace)
映射替换包含replace方法、str.replace方法(正则替换用它)、cat.codes方法,此处先主要介绍replace的用法。
通过字典或者列表进行替换:
df['Gender'].replace({'Female':0, 'Male':1})
df['Gender'].replace(['Female', 'Male'], [0, 1])
特殊的方向替换:
s = pd.Series(['a', 1, 'b', 2, 1, 1, 'a'])
s.replace([1, 2], method='ffill') # 用前面最近的未被替换的值进行替换
s.replace([1, 2], method='bfill') # 用后面最近的未被替换的值进行替换
逻辑替换(where/mask)
s = pd.Series([-1, 1.2345, 100, -50])
s.where(s<0, 100) # where函数在传入条件为False的对应行进行替换
s.where(s<0) # 当不指定替换值时,替换为缺失值。
s.mask(s<0) # mask函数在传入条件为True的对应行进行替换
s.mask(s<0, -50)
传入的条件只需是与被调用的Series索引一致的布尔序列即可:
s_condition= pd.Series([True,False,False,True],index=s.index)
s.mask(s_condition, -50)
数值替换(round/abs/clip)
s = pd.Series([-1, 1.2345, 100, -50])
s.round(2) # 按照给定进度四舍五入
s.abs() # 取绝对值
s.clip(0, 2) # 截断,前两个数分别表示上下截断边界
排序函数
值排序(sort_values)
利用set_index方法把年级和姓名两列作为索引:
df_demo = df[['Grade', 'Name', 'Height', 'Weight']].set_index(['Grade','Name'])
df_demo.head(8)
排序,默认参数ascending=True为升序:
df_demo.sort_values('Height').head(8)
df_demo.sort_values('Height', ascending=False).head(8)
多列排序
如在体重相同的情况下,对身高进行排序,且保持身高降序排列,体重升序排列:
df_demo.sort_values(['Weight','Height'],ascending=[True,False]).head()
索引排序(sort_index)
索引排序的用法和值排序完全一致,只不过元素的值在索引中,此时需要指定索引层的名字或者层号,用参数level表示。字符串的排列顺序由字母顺序决定。
df_demo.sort_index(level=['Grade','Name'],ascending=[True,False]).head(8)
apply方法
样例1:
df_demo = df[['Height', 'Weight']]
def my_mean(x):
res = x.mean()
return res
df_demo.apply(my_mean)
df_demo.apply(lambda x:x.mean())
df_demo.apply(lambda x:x.mean(), axis=1) # 逐行均值
样例2:
mad函数返回的是一个序列中偏离该序列均值的绝对值大小的均值,例如序列1,3,7,10中,均值为5.25,每一个元素偏离的绝对值为4.25,2.25,1.75,4.75,这个偏离序列的均值为3.25。现在利用apply计算升高和体重的mad指标:
df_demo.apply(lambda x:(x-x.mean()).abs().mean())
# 或
df_demo.mad()
注意:得益于传入自定义函数的处理,apply的自由度很高,但这是以性能为代价的。一般而言,使用pandas的内置函数处理和apply来处理同一个任务,其速度会相差较多,因此只有在确实存在自定义需求的情境下才考虑使用apply。
窗口对象
pandas中有3类窗口:滑动窗口rolling、扩张窗口expanding、指数加权窗口ewm。
滑动窗口
要使用滑窗函数,就必须先要对一个序列使用.rolling得到滑窗对象,其最重要的参数为窗口大小window。
s = pd.Series([1,2,3,4,5])
roller = s.rolling(window = 3)
roller
# Rolling [window=3,center=False,axis=0]
在得到了滑窗对象后,能够使用相应的聚合函数进行计算,需要注意的是窗口包含当前行所在的元素,例如在第四个位置进行均值运算时,应当计算(2+3+4)/3,而不是(1+2+3)/3:
roller.mean()
# 0 NaN
# 1 NaN
# 2 2.0
# 3 3.0
# 4 4.0
# dtype: float64
此外,还支持使用apply传入自定义函数,其传入值是对应窗口的Series,例如上述的均值函数可以等效表示:
roller.apply(lambda x:x.mean())
0 NaN
1 NaN
2 2.0
3 3.0
4 4.0
dtype: float64
roller.sum()
# 0 NaN
# 1 NaN
# 2 6.0
# 3 9.0
# 4 12.0
# dtype: float64
对于滑动相关系数或滑动协方差的计算,可以如下写出:
s2 = pd.Series([1,2,6,16,30])
roller.cov(s2)
# 0 NaN
# 1 NaN
# 2 2.5
# 3 7.0
# 4 12.0
# dtype: float64
roller.corr(s2)
# 0 NaN
# 1 NaN
# 2 0.944911
# 3 0.970725
# 4 0.995402
# dtype: float64
shift, diff, pct_change是一组类滑窗函数,它们的公共参数为periods=n,默认为1,分别表示取向前第n个元素的值、与向前第n个元素做差(与Numpy中不同,后者表示n阶差分)、与向前第n个元素相比计算增长率。这里的n可以为负,表示反方向的类似操作。
s = pd.Series([1,3,6,10,15])
s.shift(2)
# 0 NaN
# 1 NaN
# 2 1.0
# 3 3.0
# 4 6.0
# dtype: float64
s.diff(3)
# 0 NaN
# 1 NaN
# 2 NaN
# 3 9.0
# 4 12.0
# dtype: float64
s.pct_change()
# 0 NaN
# 1 2.000000
# 2 1.000000
# 3 0.666667
# 4 0.500000
# dtype: float64
s.shift(-1)
# 0 3.0
# 1 6.0
# 2 10.0
# 3 15.0
# 4 NaN
# dtype: float64
s.diff(-2)
# 0 -5.0
# 1 -7.0
# 2 -9.0
# 3 NaN
# 4 NaN
# dtype: float64
将其视作类滑窗函数的原因是,它们的功能可以用窗口大小为n+1的rolling方法等价代替:
s.rolling(3).apply(lambda x:list(x)[0]) # s.shift(2)
s.rolling(4).apply(lambda x:list(x)[-1]-list(x)[0]) # s.diff(3)
def my_pct(x):
L = list(x)
return L[-1]/L[0]-1
s.rolling(2).apply(my_pct) # s.pct_change()
扩张窗口
扩张窗口又称累计窗口,可以理解为一个动态长度的窗口,其窗口的大小就是从序列开始处到具体操作的对应位置,其使用的聚合函数会作用于这些逐步扩张的窗口上。具体地说,设序列为a1, a2, a3, a4,则其每个位置对应的窗口即[a1]、[a1, a2]、[a1, a2, a3]、[a1, a2, a3, a4]。
s = pd.Series([1, 3, 6, 10])
s.expanding().mean()
# 0 1.000000
# 1 2.000000
# 2 3.333333
# 3 5.000000
# dtype: float64
指数加权窗口
作为扩张窗口的ewm窗口
在扩张窗口中,用户可以使用各类函数进行历史的累计指标统计,但这些内置的统计函数往往把窗口中的所有元素赋予了同样的权重。事实上,可以给出不同的权重来赋给窗口中的元素,指数加权窗口就是这样一种特殊的扩张窗口。
其中,最重要的参数是alpha,它决定了默认情况下的窗口权重为 𝑤𝑖=(1−𝛼)𝑖,𝑖∈{0,1,…,𝑡} ,其中 𝑖=𝑡 表示当前元素, 𝑖=0 表示序列的第一个元素。从权重公式可以看出,离开当前值越远则权重越小,若记原序列为 𝑥 ,更新后的当前元素为 𝑦𝑡 ,此时通过加权公式归一化后可知:
对于Series而言,可以用ewm对象如下计算指数平滑后的序列:
import pandas as pd
import numpy as np
np.random.seed(1)
a=np.random.randint(-1,2,30)
print(a)
print(a.cumsum())
s = pd.Series(a.cumsum())
print(s.head())
print(s.ewm(alpha=0.2).mean().head())
# [ 0 -1 -1 0 0 -1 -1 0 -1 0 -1 1 0 1 -1 1 0 1 -1 -1 1 -1 0 1
# 1 -1 0 0 1 -1]
# [ 0 -1 -2 -2 -2 -3 -4 -4 -5 -5 -6 -5 -5 -4 -5 -4 -4 -3 -4 -5 -4 -5 -5 -4
# -3 -4 -4 -4 -3 -4]
# 0 0
# 1 -1
# 2 -2
# 3 -2
# 4 -2
# dtype: int32
# 0 0.000000
# 1 -0.555556
# 2 -1.147541
# 3 -1.436314
# 4 -1.603998
# dtype: float64
用expanding窗口实现:
import pandas as pd
import numpy as np
np.random.seed(1)
a=np.random.randint(-1,2,30)
print(a)
print(a.cumsum())
s = pd.Series(a.cumsum())
def ewm_func(x, alpha=0.2):
win = (1-alpha)**np.arange(x.shape[0])[::-1]
res = (win*x).sum()/win.sum()
return res
print(s.expanding().apply(ewm_func).head())
作为滑动窗口的ewm窗口
ewm作为一种扩张窗口的特例,只能从序列的第一个元素开始加权。现在希望给定一个限制窗口n,只对包含自身的最近的n个元素作为窗口进行滑动加权平滑。
根据滑窗函数,给出新的权重为 𝑤𝑖=(1−𝛼)𝑖,𝑖∈{0,1,…,𝑛−1} , 𝑦𝑡 更新如下:
通过rolling窗口实现这一功能:
import pandas as pd
import numpy as np
np.random.seed(1)
a=np.random.randint(-1,2,30)
print(a)
print(a.cumsum())
s = pd.Series(a.cumsum())
def ewm_func(x, alpha=0.2):
win = (1-alpha)**np.arange(x.shape[0])[::-1]
res = (win*x).sum()/win.sum()
return res
s.rolling(window=4).apply(ewm_func).head()
# 0 NaN
# 1 NaN
# 2 NaN
# 3 -1.436314
# 4 -1.826558
# dtype: float64