整章知识架构本章主要学习的pandas的相关基础,如下图
文件读写
文件读写读取文件
使用一段代码来进行演示
# 读取csv文件
df_csv = pd.read_csv('../data/my_csv.csv')
# 读取txt文件
df_txt = pd.read_table('../data/my_table.txt')
# 读取excel文件
df_excel = pd.read_excel('../data/my_excel.xlsx')
# 如果不想将第一行当作列名,可以使用header=None
df_excel = pd.read_excel('../data/my_excel.xlsx', header=None)
# index_col表示把某一列或几列作为索引
pd.read_csv('../data/my_csv.csv', index_col=['col1', 'col2'])
文件写入
一般在数据写入中,最常用的操作是把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函数,此处需要安装tabulate包。
df_csv.to_markdown()
df_csv.to_latex()
基本数据结构
基本数据结构Pandas中定义了两种基本的数据结构,Series和DataFrame
- Series
Series一般由四个部分组成,分别是序列的值data、索引index、存储类型dtype、序列的名字name。其中,索引也可以指定它的名字,默认为空。这四个属性可以通过以下的方式获取
s = pd.Series(data = [100, 'a', {'dic1':5}],
index = pd.Index(['id1', 20, 'third'], name='my_idx'),
dtype = 'object',
name = 'my_name')
# data
s.value
# 获取index
s.index
# 获取dtype
s.dtype
# 获取name
s.name
# 通过.shape可以获取序列的长度
s.shape
其中object代表一种混合数据类型
- DataFrame
DataFrame在Series的基础上增加了列索引。可以通过
[index_item]
获取对应的列的值
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'])
# 获取dataframe的值
df.value
# 获取dataframe的index
df.index
# 获取dataframe的column
df.columns
# 获取dataframe的shape
df.shape
# 获取指定的单列或多列
df['col_1'] #单列
df[['col_1', 'col_2']] #多列
# 对dataframe进行转置
df.T
常用基本函数
常用基本函数汇总函数
head
,tail
用于获取表或者序列的前n行和后n行,n的默认值是5
info
,describe
返回表的信息概况和表中数值列对应的主要统计量
在后面的章节中将会提高具有更高信息概括功能的pandas-profiling
包
df.head(5)
df.tail(5)
df.info()
df.describe()
特征统计函数
最常见的是sum, mean, median, var, std, max, min
"""以下函数均可通过axis参数指定操作的方向,默认是axis=0"""
df.sum()
df.mean()
df.median()
df.var()
df.std()
df.max()
df.min()
quantile, cout, idxmax, idxmin
返回的是分位数, 非缺失值个数, 最大值对应的索引及最小值对应的索引
唯一值函数
- 对序列使用
unique
和nunique
可以分别得到其唯一值组成的列表和唯一值的个数value_counts
可以得到唯一值和其对应出现的频数:- 如果想要观察多个列组合的唯一值,可以使用
drop_duplicates
。其中的关键参数是keep
,默认值first
表示每个组合保留第一次出现的所在行,last
表示保留最后一次出现的所在行,False
表示把所有重复组合所在的行剔除duplicated
和drop_duplicates
的功能类似,但前者返回了是否为唯一值的布尔列表,其keep
参数与后者一致。其返回的序列,把重复元素设为True
,否则为False
。drop_duplicates
等价于把duplicated
为True
的对应行剔除。
替换函数(映射替换)
- 在
replace
中,可以通过字典构造,或者传入两个列表来进行替换replace
还有一种特殊的方向替换,指定method
参数为ffill
则为用前面一个最近的未被替换的值进行替换,bfill
则使用后面最近的未被替换的值进行替换
df['Gender'].replace({'Female':0, 'Male':1}).head()
s = pd.Series(['a', 1, 'b', 2, 1, 1, 'a'])
s.replace([1, 2], method='ffill')
s.replace([1, 2], method='bfill')
替换函数(逻辑替换)
逻辑替换包括了where
和mask
,这两个函数是完全对称的:where
函数在传入条件为False
的对应行进行替换,而mask
在传入条件为True
的对应行进行替换,当不指定替换值时,替换为缺失值。clip
函数可以对超出范围的值进行截断
s = pd.Series([-1, 1.2345, 100, -50])
s.where(s<0)
s.where(s<0, 100)
s.mask(s<0)
s.mask(s<0, -50)
在 clip 中,超过边界的只能截断为边界值,如果要把超出边界的替换为自定义的值,应当如何做?
def my_clip(s:pd.Series, min_bound, max_bound, left_value, right_value)->pd.Series:
ret = s.clip(min_bound, max_bound)
ret = ret.mask(ret==min_bound, left_value)
ret = ret.mask(ret==max_bound, right_value)
return ret
排序函数
排序共有两种方式,其一为值排序,其二为索引排序,对应的函数是sort_values和sort_index
df_demo.sort_values(['Weight','Height'],ascending=[True,False]).head()
df_demo.sort_index(level=['Grade','Name'],ascending=[True,False]).head()
apply函数
6. apply方法
df_demo = df[['Height', 'Weight']]
def my_mean(x):
res = x.mean()
return res
df_demo.apply(my_mean)
apply
方法常用于DataFrame
的行迭代或者列迭代,它的axis
含义与第2小节中的统计聚合函数一致,apply
的参数往往是一个以序列为输入的函数
窗口对象
窗口对象pandas中有3类窗口,分别是滑动窗口rolling、扩张窗口expanding以及指数加权窗口ewm
s = pd.Series([1,2,3,4,5])
roller = s.rolling(window = 3)
roller
rolling对象的默认窗口方向都是向前的,某些情况下用户需要向后的窗口,例如对1,2,3设定向后窗口为2的sum操作,结果为3,5,NaN,此时应该如何实现向后的滑窗操作?(提示:使用shift)
s = pd.Series([1,2,3])
s = s.rolling(2)
s.sum().shift(-1)
cummax, cumsum, cumprod函数是典型的类扩张窗口函数,请使用expanding对象依次实现它们。
import numpy as np
# cummax
s.expanding().max()
# cumsum
s.expanding().sum()
# cumprod
s.expanding().apply(lambda x:np.prod(x))
Ex1:口袋妖怪数据集
现有一份口袋妖怪的数据集,下面进行一些背景说明:
-
#
代表全国图鉴编号,不同行存在相同数字则表示为该妖怪的不同状态 -
妖怪具有单属性和双属性两种,对于单属性的妖怪,
Type 2
为缺失值 -
Total, HP, Attack, Defense, Sp. Atk, Sp. Def, Speed
分别代表种族值、体力、物攻、防御、特攻、特防、速度,其中种族值为后6项之和
-
对
HP, Attack, Defense, Sp. Atk, Sp. Def, Speed
进行加总,验证是否为Total
值。 -
对于
#
重复的妖怪只保留第一条记录,解决以下问题:
- 求第一属性的种类数量和前三多数量对应的种类
- 求第一属性和第二属性的组合种类
- 求尚未出现过的属性组合
- 按照下述要求,构造
Series
:
- 取出物攻,超过120的替换为
high
,不足50的替换为low
,否则设为mid
- 取出第一属性,分别用
replace
和apply
替换所有字母为大写 - 求每个妖怪六项能力的离差,即所有能力中偏离中位数最大的值,添加到
df
并从大到小排序
# 验证值之和是否与Total相等
df['add'] = df[['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']].sum(axis=1)
(df['add'] == df['Total']).all()
# 去除多余的记录且只保留第一条记录
unique_df = df.drop_duplicates(['#'], keep='first')
# Type 1的种类数量
print(unique_df['Type 1'].nunique())
# Type 1前三多数量对应的种类
print(unique_df['Type 1'].value_counts().index[:3])
# 计算现在所有组合种类的数量
combine = unique_df.drop_duplicates(['Type 1', 'Type 2'])
num_unqiue_combine = combine.shape[0]
# 生成所有的组合
# 攻击值替换
df['Attack'].mask(df['Attack']>120, 'high').mask(df['Attack']<50, 'low').mask((50<=df['Attack'])&(df['Attack']<=120), 'mid').head()
# 取出第一属性
Type1 = df['Type 1']
# 使用replace进行大写替换
# 构造映射字典
map_dict = {}
for index in unique_df['Type 1'].value_counts().index:
map_dict[index] = index.upper()
Type1.replace(map_dict)
# 使用apply进行大写替换
Type1.apply(lambda s: s.upper())
# 计算六项能力的离差
df['Deviation'] = df[['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']].apply(lambda x:np.max((x-x.median()).abs()), 1)
df.sort_values('Deviation', ascending=False).head()
# 源自答案
L_full = [' '.join([i, j]) if i!=j else i for j in dp_dup['Type 1'].unique() for i in dp_dup['Type 1'].unique()]
L_part = [' '.join([i, j]) if type(j)!=float else i for i, j in zip(attr_dup['Type 1'], attr_dup['Type 2'])]
res = set(L_full).difference(set(L_part))
len(res) #
Ex2:指数加权窗口
- 作为扩张窗口的
ewm
窗口
在扩张窗口中,用户可以使用各类函数进行历史的累计指标统计,但这些内置的统计函数往往把窗口中的所有元素赋予了同样的权重。事实上,可以给出不同的权重来赋给窗口中的元素,指数加权窗口就是这样一种特殊的扩张窗口。
其中,最重要的参数是alpha
,它决定了默认情况下的窗口权重为,其中表示当前元素,表示序列的第一个元素。
从权重公式可以看出,离开当前值越远则权重越小,若记原序列为,更新后的当前元素为,此时通过加权公式归一化后可知:
对于Series
而言,可以用ewm
对象如下计算指数平滑后的序列:
np.random.seed(0)
s = pd.Series(np.random.randint(-1,2,30).cumsum())
s.head()
使用expanding窗口实现
def ewm_func(x, alpha=0.2):
weight = np.array([(1 - alpha)**i for i in range(x.shape[0]-1, -1, -1)])
return (x * weight).sum() / weight.sum()
s.expanding().apply(ewm_func).head()
- 作为滑动窗口的
ewm
窗口(参考了答案)
从第1问中可以看到,ewm
作为一种扩张窗口的特例,只能从序列的第一个元素开始加权。现在希望给定一个限制窗口n
,只对包含自身最近的n
个窗口进行滑动加权平滑。请根据滑窗函数,给出新的wi
与yt
的更新公式,并通过rolling
窗口实现这一功能。
新的权重为,更新如下:
s.rolling(window=4).apply(ewm_func).head() # 无需对原函数改动
网友评论