第一章 pandas基础-练习题
第一章 pandas基础-练习题
# 首先要导入对应的模块
import pandas as pd
import numpy as np
Ex1:口袋妖怪数据集
现有一份口袋妖怪的数据集,下面进行一些背景说明:
#代表全国图鉴编号,不同行存在相同数字则表示为该妖怪的不同状态
妖怪具有单属性和双属性两种,对于单属性的妖怪,Type 2为缺失值
Total, HP, Attack, Defense, Sp. Atk, Sp. Def, Speed分别代表种族值、体力、物攻、防御、特攻、特防、速度,其中种族值为后6项之和
# 读取数据,取前10查看数据内容
df = pd.read_csv('data/pokemon.csv')
df.head(10)
# | Name | Type 1 | Type 2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | Bulbasaur | Grass | Poison | 318 | 45 | 49 | 49 | 65 | 65 | 45 |
1 | 2 | Ivysaur | Grass | Poison | 405 | 60 | 62 | 63 | 80 | 80 | 60 |
2 | 3 | Venusaur | Grass | Poison | 525 | 80 | 82 | 83 | 100 | 100 | 80 |
3 | 3 | VenusaurMega Venusaur | Grass | Poison | 625 | 80 | 100 | 123 | 122 | 120 | 80 |
4 | 4 | Charmander | Fire | NaN | 309 | 39 | 52 | 43 | 60 | 50 | 65 |
5 | 5 | Charmeleon | Fire | NaN | 405 | 58 | 64 | 58 | 80 | 65 | 80 |
6 | 6 | Charizard | Fire | Flying | 534 | 78 | 84 | 78 | 109 | 85 | 100 |
7 | 6 | CharizardMega Charizard X | Fire | Dragon | 634 | 78 | 130 | 111 | 130 | 85 | 100 |
8 | 6 | CharizardMega Charizard Y | Fire | Flying | 634 | 78 | 104 | 78 | 159 | 115 | 100 |
9 | 7 | Squirtle | Water | NaN | 314 | 44 | 48 | 65 | 50 | 64 | 43 |
1.对HP, Attack, Defense, Sp. Atk, Sp. Def, Speed进行加总,验证是否为Total值。
2.对于#重复的妖怪只保留第一条记录,解决以下问题:
a.求第一属性的种类数量和前三多数量对应的种类
b.求第一属性和第二属性的组合种类
c.求尚未出现过的属性组合
3.按照下述要求,构造Series:
a.取出物攻,超过120的替换为high,不足50的替换为low,否则设为mid
b.取出第一属性,分别用replace和apply替换所有字母为大写
c.求每个妖怪六项能力的离差,即所有能力中偏离中位数最大的值,添加到df并从大到小排序
# 1.对HP, Attack, Defense, Sp. Atk, Sp. Def, Speed进行加总,验证是否为Total值
df_demo = df[['HP','Attack','Defense','Sp. Atk','Sp. Def','Speed']]
df_demo.sum(axis=1)==df['Total']
0 True
1 True
2 True
3 True
4 True
...
795 True
796 True
797 True
798 True
799 True
Length: 800, dtype: bool
# 或者直接用索引取出所需数据,索引从5开始
df_demo = df.columns[5:]
df[df_demo].sum(axis=1)==df['Total']
0 True
1 True
2 True
3 True
4 True
...
795 True
796 True
797 True
798 True
799 True
Length: 800, dtype: bool
# 2.对于#重复的妖怪只保留第一条记录,解决以下问题:
df_unique = df.drop_duplicates('#')
df_unique
# | Name | Type 1 | Type 2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | Bulbasaur | Grass | Poison | 318 | 45 | 49 | 49 | 65 | 65 | 45 |
1 | 2 | Ivysaur | Grass | Poison | 405 | 60 | 62 | 63 | 80 | 80 | 60 |
2 | 3 | Venusaur | Grass | Poison | 525 | 80 | 82 | 83 | 100 | 100 | 80 |
4 | 4 | Charmander | Fire | NaN | 309 | 39 | 52 | 43 | 60 | 50 | 65 |
5 | 5 | Charmeleon | Fire | NaN | 405 | 58 | 64 | 58 | 80 | 65 | 80 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
793 | 717 | Yveltal | Dark | Flying | 680 | 126 | 131 | 95 | 131 | 98 | 99 |
794 | 718 | Zygarde50% Forme | Dragon | Ground | 600 | 108 | 100 | 121 | 81 | 95 | 95 |
795 | 719 | Diancie | Rock | Fairy | 600 | 50 | 100 | 150 | 100 | 150 | 50 |
797 | 720 | HoopaHoopa Confined | Psychic | Ghost | 600 | 80 | 110 | 60 | 150 | 130 | 70 |
799 | 721 | Volcanion | Fire | Water | 600 | 80 | 110 | 120 | 130 | 90 | 70 |
721 rows × 11 columns
#a.求第一属性的种类数量
df_unique['Type 1'].nunique()
18
# 前三多数量对应的种类
df_unique['Type 1'].value_counts().head(3)
# 一般都是降序排列,直接取前三数据就能看到对应种类
Water 105
Normal 93
Grass 66
Name: Type 1, dtype: int64
# 也可以取前三多数量对应的种类索引值
df_unique['Type 1'].value_counts().index[:3]
Index(['Water', 'Normal', 'Grass'], dtype='object')
# b.求第一属性和第二属性的组合种类
# 直白点说就是相当于excel的删除重复性,第一属性&第二属性求唯一
df_unique.drop_duplicates(['Type 1','Type 2']).shape
# 查看行数和列数
(143, 11)
# 只想看行数取对应的索引值
df_unique.drop_duplicates(['Type 1','Type 2']).shape[0]
# 查看列数.shape[1]
143
# c.求尚未出现过的属性组合
# type1有18种,type2有可能会缺失(单属性),所以type2有19种,18*19=342种属性
# 用语句操作
all_type = [i + ' ' +j for i in df['Type 1'].unique() for j in (df['Type 1'].unique().tolist()+[''])]
len(all_type)
342
# 组合是单行数据应该用zip,且type2里有缺失值,字符不能直接相加,应该替换成空字符串
had_type= [i+' '+j for i,j in zip(df['Type 1'],df['Type 2'].replace(np.nan,''))]
# 求未出现的,找出全部组合和已有组合的不同集合
not_had_type = set(all_type).difference(set(had_type))
len(not_had_type)
188
# 3.按照下述要求,构造Series:
# a.取出物攻,超过120的替换为high,不足50的替换为low,否则设为mid
df['Attack'].mask(df['Attack']>120,'high').mask(df['Attack']<50,'low').mask((df['Attack']<=120)&(df['Attack']>=50),'mid')
0 low
1 mid
2 mid
3 mid
4 mid
...
795 mid
796 high
797 mid
798 high
799 mid
Name: Attack, Length: 800, dtype: object
# b.取出第一属性,分别用replace和apply替换所有字母为大写
# 用replace替换
df_demo = df['Type 1'].unique().tolist()
df['Type 1'].replace(df_demo,[str.upper(i) for i in df_demo])
0 GRASS
1 GRASS
2 GRASS
3 GRASS
4 FIRE
...
795 ROCK
796 ROCK
797 PSYCHIC
798 PSYCHIC
799 FIRE
Name: Type 1, Length: 800, dtype: object
# 用apply 逐个元素进行处理替换
df['Type 1'].apply(lambda x:str.upper(x))
0 GRASS
1 GRASS
2 GRASS
3 GRASS
4 FIRE
...
795 ROCK
796 ROCK
797 PSYCHIC
798 PSYCHIC
799 FIRE
Name: Type 1, Length: 800, dtype: object
#c.求每个妖怪六项能力的离差,即所有能力中偏离中位数最大的值,添加到df并从大到小排序
df['新的一列'] = df[df.columns[5:]].apply(lambda x:(x-x.median()).max(),axis=1)
df.sort_values('新的一列',ascending= False)
# | Name | Type 1 | Type 2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | 新的一列 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
230 | 213 | Shuckle | Bug | Rock | 505 | 20 | 10 | 230 | 10 | 230 | 5 | 215.0 |
121 | 113 | Chansey | Normal | NaN | 450 | 250 | 5 | 5 | 35 | 105 | 50 | 207.5 |
261 | 242 | Blissey | Normal | NaN | 540 | 255 | 10 | 10 | 75 | 135 | 55 | 190.0 |
333 | 306 | AggronMega Aggron | Steel | NaN | 630 | 70 | 140 | 230 | 60 | 80 | 50 | 155.0 |
224 | 208 | SteelixMega Steelix | Steel | Ground | 610 | 75 | 125 | 230 | 55 | 95 | 30 | 145.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
552 | 493 | Arceus | Normal | NaN | 720 | 120 | 120 | 120 | 120 | 120 | 120 | 0.0 |
760 | 690 | Skrelp | Poison | Water | 320 | 50 | 60 | 60 | 60 | 60 | 30 | 0.0 |
538 | 481 | Mesprit | Psychic | NaN | 580 | 80 | 105 | 105 | 105 | 105 | 80 | 0.0 |
547 | 489 | Phione | Water | NaN | 480 | 80 | 80 | 80 | 80 | 80 | 80 | 0.0 |
165 | 151 | Mew | Psychic | NaN | 600 | 100 | 100 | 100 | 100 | 100 | 100 | 0.0 |
800 rows × 12 columns
Ex2:指数加权窗口
1、作为扩张窗口的ewm窗口
2、在扩张窗口中,用户可以使用各类函数进行历史的累计指标统计,但这些内置的统计函数往往把窗口中的所有元素赋予了同样的权重。事实上,可以给出不同的权重来赋给窗口中的元素,指数加权窗口就是这样一种特殊的扩张窗口。
其中,最重要的参数是alpha,它决定了默认情况下的窗口权重为 wi=(1−α)i,i∈{0,1,…,t}wi=(1−α)i,i∈{0,1,…,t} ,其中 i=t 表示当前元素, i=0 表示序列的第一个元素。
从权重公式可以看出,离开当前值越远则权重越小,若记原序列为 xx ,更新后的当前元素为 ytyt ,此时通过加权公式归一化后可知:
对于Series而言,可以用ewm对象如下计算指数平滑后的序列:
np.random.seed(0)
s = pd.Series(np.random.randint(-1,2,30).cumsum())
s.head()
0 -1
1 -1
2 -2
3 -2
4 -2
dtype: int32
s.ewm(alpha=0.2).mean().head()
0 -1.000000
1 -1.000000
2 -1.409836
3 -1.609756
4 -1.725845
dtype: float64
请用expanding窗口实现。
1、作为滑动窗口的ewm窗口
从第1问中可以看到,ewm作为一种扩张窗口的特例,只能从序列的第一个元素开始加权。现在希望给定一个限制窗口n,只对包含自身最近的n个窗口进行滑动加权平滑。请根据滑窗函数,给出新的wi与yt的更新公式,并通过rolling窗口实现这一功能。
def ewm_func(x, alpha=0.2):
win = (1-alpha)**np.arange(x.shape[0])[::-1]# x.shape[0]每次✖的指数从0开始到x
#np.arange(x.shape[0]),从0到x.shape[0]-1,对应1-alpha的几次幂
res = (win*x).sum()/win.sum()
return res
s.expanding().apply(ewm_func).head()
0 -1.000000
1 -1.000000
2 -1.409836
3 -1.609756
4 -1.725845
dtype: float64
s.rolling(window=4).apply(ewm_func).head() # 无需对原函数改动
0 NaN
1 NaN
2 NaN
3 -1.609756
4 -1.826558
dtype: float64