第二章 pandas基础
【练一练】
rolling
对象的默认窗口方向都是向前的,某些情况下用户需要向后的窗口,例如对1,2,3设定向后窗口为2的sum
操作,结果为3,5,NaN,此时应该如何实现向后的滑窗操作?(提示:使用shift
)
s = pd.Series([1, 3, 6, 10])
s.expanding().mean()
Ex1:口袋妖怪数据集
现有一份口袋妖怪的数据集,下面进行一些背景说明:
#
代表全国图鉴编号,不同行存在相同数字则表示为该妖怪的不同状态- 妖怪具有单属性和双属性两种,对于单属性的妖怪,
Type 2
为缺失值 Total, HP, Attack, Defense, Sp. Atk, Sp. Def, Speed
分别代表种族值、体力、物攻、防御、特攻、特防、速度,其中种族值为后6项之和
import numpy as np
import pandas as pd
df = pd.read_csv('../data/pokemon.csv')
df.head(3)
- 对
HP, Attack, Defense, Sp. Atk, Sp. Def, Speed
进行加总,验证是否为Total
值。 - 对于
#
重复的妖怪只保留第一条记录,解决以下问题:
- 求第一属性的种类数量和前三多数量对应的种类
- 求第一属性和第二属性的组合种类
- 求尚未出现过的属性组合
- 按照下述要求,构造
Series
:
- 取出物攻,超过120的替换为
high
,不足50的替换为low
,否则设为mid
- 取出第一属性,分别用
replace
和apply
替换所有字母为大写 - 求每个妖怪六项能力的离差,即所有能力中偏离中位数最大的值,添加到
df
并从大到小排序
#1. 对HP, Attack, Defense, Sp. Atk, Sp. Def, Speed进行加总,验证是否为Total值。
(df.loc[:,'HP':'Speed'].sum(axis =1) == df['Total']).all()
#2.对于#重复的妖怪只保留第一条记录
print(df.shape) # 原数据框维度 (800*11)
df1 = df.drop_duplicates(['#'],keep = 'first')
df1.reset_index(drop=True, inplace=True)
print(df1.shape) # 去重后数据框维度(721*11)
#3. 求第一属性的种类数量和前三多数量对应的种类
type1 = df1['Type 1'].unique()
type2 = df1['Type 2'].unique()
type2_num = df1['Type 2'].nunique()
print(len(type1)) # 第一属性的种类数量:18
print(type2_num) # 第二属性的种类数量,不包含空值:18
print(len(type2)) # 第二属性的种类数量,包含空值:19
print(set(type2) - set(type1)) # 第一属性和第二属性一样
df1['Type 1'].value_counts().head(3) # 前三多数量对应的种类
#4. 求第一属性和第二属性的组合种类
df_2type = df1.dropna(subset = ['Type 2']) # 去除第二属性为空的项目
df_2type.reset_index(drop=True, inplace=True) # 索引重排序
type_list = [] # 存放属性组合
for i in range(df_2type.shape[0]):
tp = (df_2type.loc[i,'Type 1'],df_2type.loc[i,'Type 2'])
type_list.append(tp)
def type_comnbination_count(type_list): # 属性组合的去重:考虑('a','b')组合和('b','a')组合等效
combine = []
for tp in type_list:
l,r = tp
re_tp = (r,l)
if (tp not in combine) and (re_tp not in combine):
combine.append(tp)
return combine
mixtype = type_comnbination_count(type_list)
print(len(mixtype)) # 第一属性和第二属性的组合种类数目:109
#5.求尚未出现过的属性组合
#理论组合,18个组合中取两个:
theory_count = 18*17/2 # 153
count_dif = theory_count - len(mixtype)
print(count_dif)
#6. 取出物攻,超过120的替换为high,不足50的替换为low,否则设为mid
def attack_level(x):
level = None
if x > 120:
level = "high"
elif x < 50:
level = "low"
else:
level = "mid"
return level
df1['Attack'].apply(attack_level)
#7. 取出第一属性,分别用replace和apply替换所有字母为大写
type1_list = df1['Type 1'].unique().tolist()
method1 = df1['Type 1'].replace(type1_list,[str.upper(i) for i in type1_list])
method2 = df1['Type 1'].apply(lambda x:x.upper())
(method1 == method2).all()
#8. 求每个妖怪六项能力的离差,即所有能力中偏离中位数最大的值,添加到df并从大到小排序
atts_medium_diffmax = []
for i in range(df1.shape[0]):
att_ary = np.array(df1.loc[i,'HP':].tolist())
att_medium_diff = np.abs(att_ary - np.nanmedian(att_ary))
att_medium_diffmax = att_ary[np.nanargmax(att_medium_diff)]
atts_medium_diffmax.append(att_medium_diffmax)
df1['atts_diffmax'] = atts_medium_diffmax
df1.sort_values('atts_diffmax',ascending=False)
Ex2:指数加权窗口
- 作为扩张窗口的
ewm
窗口
在扩张窗口中,用户可以使用各类函数进行历史的累计指标统计,但这些内置的统计函数往往把窗口中的所有元素赋予了同样的权重。事实上,可以给出不同的权重来赋给窗口中的元素,指数加权窗口就是这样一种特殊的扩张窗口。
其中,最重要的参数是alpha
,它决定了默认情况下的窗口权重为
w
i
=
(
1
−
α
)
i
,
i
∈
{
0
,
1
,
.
.
.
,
t
}
w_i=(1−\alpha)^i,i\in\{0,1,...,t\}
wi=(1−α)i,i∈{0,1,...,t},其中
i
=
t
i=t
i=t表示当前元素,
i
=
0
i=0
i=0表示序列的第一个元素。
从权重公式可以看出,离开当前值越远则权重越小,若记原序列为
x
x
x,更新后的当前元素为
y
t
y_t
yt,此时通过加权公式归一化后可知:
对于Series
而言,可以用ewm
对象如下计算指数平滑后的序列:
np.random.seed(0)
ary = np.random.randint(-1,2,30)
s = pd.Series(ary.cumsum())
print(ary)
print(s.head())
s.ewm(alpha=0.2)
#ewm函数返回ExponentialMovingWindow对象,min_periods=1即滑窗的最小size=1,adjust=True即利用上式和alpha参数计算每项的权重
s.ewm(alpha=0.2).mean().head()
用expanding
窗口实现。
def ewm_func(x,alpha = 0.2):
weight = (1-alpha)**np.arange(len(x))[::-1]
res = (x*weight).sum()/sum(weight)
return res
s.expanding().apply(ewm_func).head()
2. 作为滑动窗口的ewm
窗口
从第1问中可以看到,ewm
作为一种扩张窗口的特例,只能从序列的第一个元素开始加权。现在希望给定一个限制窗口n
,只对包含自身最近的n
个窗口进行滑动加权平滑。请根据滑窗函数,给出新的wi
与yt
的更新公式,并通过rolling
窗口实现这一功能。
s.rolling(window = 4).apply(ewm_func).head()