第二章 Pandsa基础

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

常用基本函数

常用基本函数

汇总函数
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:口袋妖怪数据集

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

  • #代表全国图鉴编号,不同行存在相同数字则表示为该妖怪的不同状态

  • 妖怪具有单属性和双属性两种,对于单属性的妖怪,Type 2为缺失值

  • Total, HP, Attack, Defense, Sp. Atk, Sp. Def, Speed分别代表种族值、体力、物攻、防御、特攻、特防、速度,其中种族值为后6项之和

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

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

  • 求第一属性的种类数量和前三多数量对应的种类
  • 求第一属性和第二属性的组合种类
  • 求尚未出现过的属性组合
  1. 按照下述要求,构造Series
  • 取出物攻,超过120的替换为high,不足50的替换为low,否则设为mid
  • 取出第一属性,分别用replaceapply替换所有字母为大写
  • 求每个妖怪六项能力的离差,即所有能力中偏离中位数最大的值,添加到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:指数加权窗口

  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() # 无需对原函数改动
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容