第二章 Pandsa基础

2020-12-19  本文已影响0人  陈易男

本章主要学习的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

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在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

常用基本函数

常用基本函数

汇总函数
headtail用于获取表或者序列的前n行和后n行,n的默认值是5
infodescribe返回表的信息概况和表中数值列对应的主要统计量
在后面的章节中将会提高具有更高信息概括功能的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返回的是分位数, 非缺失值个数, 最大值对应的索引及最小值对应的索引

唯一值函数

  1. 对序列使用uniquenunique可以分别得到其唯一值组成的列表和唯一值的个数
  2. value_counts可以得到唯一值和其对应出现的频数:
  3. 如果想要观察多个列组合的唯一值,可以使用drop_duplicates。其中的关键参数是keep,默认值first表示每个组合保留第一次出现的所在行,last表示保留最后一次出现的所在行,False表示把所有重复组合所在的行剔除
  4. duplicateddrop_duplicates的功能类似,但前者返回了是否为唯一值的布尔列表,其keep参数与后者一致。其返回的序列,把重复元素设为True,否则为Falsedrop_duplicates等价于把duplicatedTrue的对应行剔除。

替换函数(映射替换)

  1. replace中,可以通过字典构造,或者传入两个列表来进行替换
  2. 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')

替换函数(逻辑替换)
逻辑替换包括了wheremask,这两个函数是完全对称的: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)

\color{red}{练一练}
在 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

\color{red}{练一练}
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))

\color{red}{练习}

Ex1:口袋妖怪数据集

现有一份口袋妖怪的数据集,下面进行一些背景说明:

  1. HP, Attack, Defense, Sp. Atk, Sp. Def, Speed进行加总,验证是否为Total值。

  2. 对于#重复的妖怪只保留第一条记录,解决以下问题:

  1. 按照下述要求,构造Series
# 验证值之和是否与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:指数加权窗口

  1. 作为扩张窗口的ewm窗口

在扩张窗口中,用户可以使用各类函数进行历史的累计指标统计,但这些内置的统计函数往往把窗口中的所有元素赋予了同样的权重。事实上,可以给出不同的权重来赋给窗口中的元素,指数加权窗口就是这样一种特殊的扩张窗口。

其中,最重要的参数是alpha,它决定了默认情况下的窗口权重为w_i=(1−\alpha)^i,i\in\{0,1,...,t\},其中i=t表示当前元素,i=0表示序列的第一个元素。

从权重公式可以看出,离开当前值越远则权重越小,若记原序列为x,更新后的当前元素为y_t,此时通过加权公式归一化后可知:

\begin{split}y_t &=\frac{\sum_{i=0}^{t} w_i x_{t-i}}{\sum_{i=0}^{t} w_i} \\ &=\frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + (1 - \alpha)^{t} x_{0}}{1 + (1 - \alpha) + (1 - \alpha)^2 + (1 - \alpha)^{t-1}}\\\end{split}

对于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()
  1. 作为滑动窗口的ewm窗口(参考了答案)

从第1问中可以看到,ewm作为一种扩张窗口的特例,只能从序列的第一个元素开始加权。现在希望给定一个限制窗口n,只对包含自身最近的n个窗口进行滑动加权平滑。请根据滑窗函数,给出新的wiyt的更新公式,并通过rolling窗口实现这一功能。

新的权重为w_i = (1 - \alpha)^i, i\in \{0,1,...,n-1\}y_t更新如下:
\begin{split}y_t &=\frac{\sum_{i=0}^{n-1} w_i x_{t-i}}{\sum_{i=0}^{n-1} w_i} \\ &=\frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + (1 - \alpha)^{n-1} x_{t-(n-1)}}{1 + (1 - \alpha) + (1 - \alpha)^2 + (1 - \alpha)^{n-1}}\\\end{split}

s.rolling(window=4).apply(ewm_func).head() # 无需对原函数改动
上一篇下一篇

猜你喜欢

热点阅读