import numpy as np
import pandas as pd
pd.__version__
'1.1.5'
2.1.1
df_csv = pd.read_csv('my_csv.csv')
df_csv
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
0 | 2 | a | 1.4 | apple | 2020/1/1 |
1 | 3 | b | 3.4 | banana | 2020/1/2 |
2 | 6 | c | 2.5 | orange | 2020/1/5 |
3 | 5 | d | 3.2 | lemon | 2020/1/7 |
df_txt = pd.read_table('my_table.txt')
df_txt
col1 | col2 | col3 | col4 | |
---|---|---|---|---|
0 | 2 | a | 1.4 | apple 2020/1/1 |
1 | 3 | b | 3.4 | banana 2020/1/2 |
2 | 6 | c | 2.5 | orange 2020/1/5 |
3 | 5 | d | 3.2 | lemon 2020/1/7 |
df_excel = pd.read_excel('my_excel.xlsx')
df_excel
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
0 | 2 | a | 1.4 | apple | 2020/1/1 |
1 | 3 | b | 3.4 | banana | 2020/1/2 |
2 | 6 | c | 2.5 | orange | 2020/1/5 |
3 | 5 | d | 3.2 | lemon | 2020/1/7 |
# header = None表示第一行不作为列名,相当于新增一行标识列号
pd.read_table('my_table.txt', header=None)
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | col1 | col2 | col3 | col4 |
1 | 2 | a | 1.4 | apple 2020/1/1 |
2 | 3 | b | 3.4 | banana 2020/1/2 |
3 | 6 | c | 2.5 | orange 2020/1/5 |
4 | 5 | d | 3.2 | lemon 2020/1/7 |
# index_col 表示把某一列或几列作为索引
# 理解是做group by?把['col2', 'col4']及对应的值当做一组,放到最左侧
# 然后用\n将这一组和其他的分开
pd.read_csv('my_csv.csv', index_col=['col2', 'col4'])
col1 | col3 | col5 | ||
---|---|---|---|---|
col2 | col4 | |||
a | apple | 2 | 1.4 | 2020/1/1 |
b | banana | 3 | 3.4 | 2020/1/2 |
c | orange | 6 | 2.5 | 2020/1/5 |
d | lemon | 5 | 3.2 | 2020/1/7 |
# usecols 表示读取列的集合,默认读取所有的列
# 可以理解为,只有col1和col2的列is_use=True,所以不设定第二个参数时,所有列都会展示
pd.read_table('my_table.txt', usecols=['col1', 'col2'])
col1 | col2 | |
---|---|---|
0 | 2 | a |
1 | 3 | b |
2 | 6 | c |
3 | 5 | d |
# parse_dates 表示需要转化为时间的列
# 原列时间格式为2020/1/1,现在转为时间格式为2020-01-01
# 对于其他列的值,无法解析成时间格式
pd.read_csv('my_csv.csv', parse_dates=['col5'])
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
0 | 2 | a | 1.4 | apple | 2020-01-01 |
1 | 3 | b | 3.4 | banana | 2020-01-02 |
2 | 6 | c | 2.5 | orange | 2020-01-05 |
3 | 5 | d | 3.2 | lemon | 2020-01-07 |
# nrows 表示读取的数据行
# 默认从第0行开始读取
pd.read_excel('my_excel.xlsx', nrows=2)
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
0 | 2 | a | 1.4 | apple | 2020/1/1 |
1 | 3 | b | 3.4 | banana | 2020/1/2 |
在读取 txt 文件时,经常遇到分隔符非空格的情况,read_table 有一个分割参数 sep ,它使得用户可以自定义分割符号,进行 txt 数据的读取。
# 读取的表以 |||| 为分割
pd.read_table('my_table_special_sep.txt')
col1 |||| col2 | |
---|---|
0 | TS |||| This is an apple. |
1 | GQ |||| My name is Bob. |
2 | WT |||| Well done! |
3 | PT |||| May I help you? |
# 可以使用 sep ,同时需要指定引擎为 python
# 在使用 read_table 的时候需要注意,参数 sep 中使用的是正则表达式,因此需要对 | 进行转义 变成 \| ,否则无法读取到正确的结果
# sep定义的直接是正则式的内容,匹配到该正则式的内容,则出去
pd.read_table('my_table_special_sep.txt',sep=' \|\|\|\| ',engine='python')
col1 | col2 | |
---|---|---|
0 | TS | This is an apple. |
1 | GQ | My name is Bob. |
2 | WT | Well done! |
3 | PT | May I help you? |
2.1.2 数据写入
# 把 index 设置为 False,特别当索引没有特殊意义的时候,这样的行为 能把索引在保存的时候去除。
df_csv.to_csv('my_csv_saved.csv', index=False)
df_excel.to_excel('my_excel_saved.xlsx', index=False)
df_txt.to_csv('my_txt_saved.txt', sep='\t', index=False)
# 把表格转换为markdown格式
print(df_csv.to_markdown())
| | col1 | col2 | col3 | col4 | col5 |
|---:|-------:|:-------|-------:|:-------|:---------|
| 0 | 2 | a | 1.4 | apple | 2020/1/1 |
| 1 | 3 | b | 3.4 | banana | 2020/1/2 |
| 2 | 6 | c | 2.5 | orange | 2020/1/5 |
| 3 | 5 | d | 3.2 | lemon | 2020/1/7 |
# 把表格转换为latex格式
print(df_csv.to_latex())
\begin{tabular}{lrlrll}
\toprule
{} & col1 & col2 & col3 & col4 & col5 \\
\midrule
0 & 2 & a & 1.4 & apple & 2020/1/1 \\
1 & 3 & b & 3.4 & banana & 2020/1/2 \\
2 & 6 & c & 2.5 & orange & 2020/1/5 \\
3 & 5 & d & 3.2 & lemon & 2020/1/7 \\
\bottomrule
\end{tabular}
2.2 基本数据结构
pandas中具有两种基本的数据存储结构,存储一维values的Series和存储二维values的DataFrame
Series一般由四个部分组成,分别是序列的值data,索引index,存储类型dtype,序列的名字name
其中,索引也可以指定它的名字,默认为空。
a = pd.Series(data = [100, 'a', {'dict1':5}],
index = pd.Index(['id1',20,'third'], name='my_idx'),
dtype = 'object',
name = 'my_name')
a
my_idx
id1 100
20 a
third {'dict1': 5}
Name: my_name, dtype: object
object 类型 object代表了一种混合类型,正如上面的例子中存储了整数、字符串以及Python的字典数据结构。
此外,目前pandas把纯字符串序列也默认认为是一种object类型的序列,但它也可以用string类型存储。
# 属性可以通过.的方式来获取
# 返回array类型的结果,类型是混合类型object
a.values
array([100, 'a', {'dict1': 5}], dtype=object)
# index会返回索引,object类型和序列名字
a.index
Index(['id1', 20, 'third'], dtype='object', name='my_idx')
# 表示是一个python对象
a.dtype
dtype('O')
a.name
'my_name'
# (3,)表示第一维是3,第二个参数缺省
# 获取序列的长度
a.shape
(3,)
# 取出单个索引对应的值,[]中是索引
a['third']
{'dict1': 5}
DataFrame在Series 的基础上增加了列索引,一个数据框可以由二维的data与行列索引来构造
# 在字符串中用%d,类似于.format的用法,就是规定字符串中的数可以由变量来表示
# 字符串中 %d规定输出的是数字,字符串外层也用%i来表示变量
# 另一种是用枚举方式表示列值
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'])
df
col_0 | col_1 | col_2 | |
---|---|---|---|
row_0 | 1 | a | 1.2 |
row_1 | 2 | b | 2.2 |
row_2 | 3 | c | 3.2 |
但一般而言,更多的时候会采用从列索引名到数据的映射来构造数据框,同时再加上行索引
df = pd.DataFrame(data = {'col_0':[1,2,3],'col_1':list('abc'),'col_2':[1.2,2.2,3.2]},
index = ['row_%d' %i for i in range(3)])
df
col_0 | col_1 | col_2 | |
---|---|---|---|
row_0 | 1 | a | 1.2 |
row_1 | 2 | b | 2.2 |
row_2 | 3 | c | 3.2 |
在DataFrame中可以用[col_name]与[col_list]来取出相应的列与由多个列组成的表,结果分别为 Series和DataFrame
# 相当于只取col_0这一列的值
df['col_0']
row_0 1
row_1 2
row_2 3
Name: col_0, dtype: int64
# 用list表示取多列的值
df[['col_0','col_2']]
col_0 | col_2 | |
---|---|---|
row_0 | 1 | 1.2 |
row_1 | 2 | 2.2 |
row_2 | 3 | 3.2 |
df.values
array([[1, 'a', 1.2],
[2, 'b', 2.2],
[3, 'c', 3.2]], dtype=object)
df.index
Index(['row_0', 'row_1', 'row_2'], dtype='object')
df.columns
Index(['col_0', 'col_1', 'col_2'], dtype='object')
# 返回的是值为相应列数据类型的 Series
df.dtypes
col_0 int64
col_1 object
col_2 float64
dtype: object
# 可以把DataFrame进行转置
df.T
row_0 | row_1 | row_2 | |
---|---|---|---|
col_0 | 1 | 2 | 3 |
col_1 | a | b | c |
col_2 | 1.2 | 2.2 | 3.2 |
2.3 常用基本函数
df = pd.read_csv('learn_pandas.csv')
# 上述列名依次代表学校、年级、姓名、性别、身高、体重、是否为转系生、体测场次、测试时间、1000 米成绩
df.columns
Index(['School', 'Grade', 'Name', 'Gender', 'Height', 'Weight', 'Transfer',
'Test_Number', 'Test_Date', 'Time_Record'],
dtype='object')
# df整张表中,取表的前7列
df = df[df.columns[:7]]
df
School | Grade | Name | Gender | Height | Weight | Transfer | |
---|---|---|---|---|---|---|---|
0 | Shanghai Jiao Tong University | Freshman | Gaopeng Yang | Female | 158.9 | 46.0 | N |
1 | Peking University | Freshman | Changqiang You | Male | 166.5 | 70.0 | N |
2 | Shanghai Jiao Tong University | Senior | Mei Sun | Male | 188.9 | 89.0 | N |
3 | Fudan University | Sophomore | Xiaojuan Sun | Female | NaN | 41.0 | N |
4 | Fudan University | Sophomore | Gaojuan You | Male | 174.0 | 74.0 | N |
... | ... | ... | ... | ... | ... | ... | ... |
195 | Fudan University | Junior | Xiaojuan Sun | Female | 153.9 | 46.0 | N |
196 | Tsinghua University | Senior | Li Zhao | Female | 160.9 | 50.0 | N |
197 | Shanghai Jiao Tong University | Senior | Chengqiang Chu | Female | 153.9 | 45.0 | N |
198 | Shanghai Jiao Tong University | Senior | Chengmei Shen | Male | 175.3 | 71.0 | N |
199 | Tsinghua University | Sophomore | Chunpeng Lv | Male | 155.7 | 51.0 | N |
200 rows × 7 columns
2.3.1 汇总函数
# head,tail函数分别表示返回表或者序列的前n行和后n行,其中n默认为 5
df.head(4)
School | Grade | Name | Gender | Height | Weight | Transfer | |
---|---|---|---|---|---|---|---|
0 | Shanghai Jiao Tong University | Freshman | Gaopeng Yang | Female | 158.9 | 46.0 | N |
1 | Peking University | Freshman | Changqiang You | Male | 166.5 | 70.0 | N |
2 | Shanghai Jiao Tong University | Senior | Mei Sun | Male | 188.9 | 89.0 | N |
3 | Fudan University | Sophomore | Xiaojuan Sun | Female | NaN | 41.0 | N |
df.tail(4)
School | Grade | Name | Gender | Height | Weight | Transfer | |
---|---|---|---|---|---|---|---|
196 | Tsinghua University | Senior | Li Zhao | Female | 160.9 | 50.0 | N |
197 | Shanghai Jiao Tong University | Senior | Chengqiang Chu | Female | 153.9 | 45.0 | N |
198 | Shanghai Jiao Tong University | Senior | Chengmei Shen | Male | 175.3 | 71.0 | N |
199 | Tsinghua University | Sophomore | Chunpeng Lv | Male | 155.7 | 51.0 | N |
# info返回表的信息概况
# info主要展示列的信息, 统计每列有多少非空,以及数据类型
df.info()
RangeIndex: 200 entries, 0 to 199
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 School 200 non-null object
1 Grade 200 non-null object
2 Name 200 non-null object
3 Gender 200 non-null object
4 Height 183 non-null float64
5 Weight 189 non-null float64
6 Transfer 188 non-null object
dtypes: float64(2), object(5)
memory usage: 11.1+ KB
# describe返回的是表中数值列对应的主要统计量
# 计数,平均值,标准差,最小值,最大值以及较低的百分位数和50。默认情况下,较低的百分位数为25,较高的百分位数为75.50百分位数与中位数相同
df.describe()
Height | Weight | |
---|---|---|
count | 183.000000 | 189.000000 |
mean | 163.218033 | 55.015873 |
std | 8.608879 | 12.824294 |
min | 145.400000 | 34.000000 |
25% | 157.150000 | 46.000000 |
50% | 161.900000 | 51.000000 |
75% | 167.500000 | 65.000000 |
max | 193.900000 | 89.000000 |
# 这个参数默认是只计算数值型特征的统计量,当输入include=['O'],会计算离散型变量的统计特征
# 直接给出了非空数量count,唯一值种类unique,出现最多的类型top和出现次数freq
df.describe(include = ['O'])
School | Grade | Name | Gender | Transfer | |
---|---|---|---|---|---|
count | 200 | 200 | 200 | 200 | 188 |
unique | 4 | 4 | 170 | 2 | 2 |
top | Tsinghua University | Junior | Xiaoqiang Qin | Female | N |
freq | 69 | 59 | 3 | 141 | 181 |
2.3.2 特征统计函数
操作后返回的是标量,所以又称为聚合函数,它们有一个公共参数axis
默认0代表逐列聚合,设为则表示逐行聚合
df_stat = df[['Height','Weight']]
# mean()求平均
df_stat.mean()
Height 163.218033
Weight 55.015873
dtype: float64
# max()求最大值
df_stat.max()
Height 193.9
Weight 89.0
dtype: float64
# 分位数,分位数指的就是连续分布函数中的一个点,这个点对应概率p
df_stat.quantile(0.75)
Height 167.5
Weight 65.0
Name: 0.75, dtype: float64
# 非缺失值个数,统计有值的函数
df_stat.count()
Height 183
Weight 189
dtype: int64
# 最大值对应的索引!是索引,不是数值
df_stat.idxmax()
Height 193
Weight 2
dtype: int64
# 最小值对应的索引
df_stat.idxmin()
Height 143
Weight 49
dtype: int64
# 但在这个数据集上求行(体重和身高)的均值,没有太大意义
df_stat.mean(axis=1).head()
0 102.45
1 118.25
2 138.95
3 41.00
4 124.00
dtype: float64
2.3.3 唯一值函数
# 对序列使用unique可以得到其唯一值组成的列表
df['School'].unique()
array(['Shanghai Jiao Tong University', 'Peking University',
'Fudan University', 'Tsinghua University'], dtype=object)
# 对序列使用nunique可以得到唯一值的个数
df['School'].nunique()
4
# value_counts可以得到某一列的唯一值和其对应出现的频数
df['School'].value_counts()
Tsinghua University 69
Shanghai Jiao Tong University 57
Fudan University 40
Peking University 34
Name: School, dtype: int64
# drop_duplicates可以观察多个列组合的唯一值
# keep参数,默认值first表示每个组合保留第一次出现的所在行
# last表示保留最后一次出现的所在行
# false表示把所有重复组合所在的行剔除
# drop_duplicates返回了唯一值的布尔列表
df_stat = df[['Gender','Transfer','Name']]
df_stat.drop_duplicates(['Gender','Transfer'])
Gender | Transfer | Name | |
---|---|---|---|
0 | Female | N | Gaopeng Yang |
1 | Male | N | Changqiang You |
12 | Female | NaN | Peng You |
21 | Male | NaN | Xiaopeng Shen |
36 | Male | Y | Xiaojuan Qin |
43 | Female | Y | Gaoli Feng |
df_stat.drop_duplicates(['Gender','Transfer'], keep='last')
Gender | Transfer | Name | |
---|---|---|---|
147 | Male | NaN | Juan You |
150 | Male | Y | Chengpeng You |
169 | Female | Y | Chengquan Qin |
194 | Female | NaN | Yanmei Qian |
197 | Female | N | Chengqiang Chu |
199 | Male | N | Chunpeng Lv |
# 保留只出现过一次的性别和姓名的组合
df_stat.drop_duplicates(['Name','Gender'], keep=False).head()
Gender | Transfer | Name | |
---|---|---|---|
0 | Female | N | Gaopeng Yang |
1 | Male | N | Changqiang You |
2 | Male | N | Mei Sun |
4 | Male | N | Gaojuan You |
5 | Female | N | Xiaoli Qian |
df['School'].drop_duplicates()
0 Shanghai Jiao Tong University
1 Peking University
3 Fudan University
5 Tsinghua University
Name: School, dtype: object
df_stat.duplicated(['Gender', 'Transfer']).head()
0 False
1 False
2 True
3 True
4 True
dtype: bool
# duplicate把重复元素设为True,否则为False
# drop_duplicates等价于把 duplicated为True 的对应行剔除
# 有重复值是True,没重复值是False
# 筛选Gender和Transfer的组合种类
df_stat.duplicated(['Gender','Transfer']).head()
0 False
1 False
2 True
3 True
4 True
dtype: bool
df['School'].duplicated().head()
0 False
1 False
2 True
3 False
4 True
Name: School, dtype: bool
2.3.4 替换函数
替换操作是针对某一个列进行的
映射替换、逻辑替换、数值替换
# 在 replace 中,可以通过字典构造,或者传入两个列表来进行替换
df['Gender'].replace({'Female':0, 'Male':1}).head()
0 0
1 1
2 1
3 0
4 1
Name: Gender, dtype: int64
# replace中的参数相当于,第一个参数列表是字典的 key,第二个参数列表是字典的value
df['Gender'].replace(['Female','Male'],[0,1]).head()
0 0
1 1
2 1
3 0
4 1
Name: Gender, dtype: int64
replace还有一种特殊的方向替换,指定method参数为ffill,则为用前面一个最近的未被替换的值进行替换;bfill则使用后面最近的未被替换的值进行替换
# 把1和2替换成前面的值
a = pd.Series(['a',1,'b',2,1,1,'a'])
a.replace([1,2], method='ffill')
0 a
1 a
2 b
3 b
4 b
5 b
6 a
dtype: object
# 把1,2替换成后面的值
a.replace([1,2], method='bfill')
0 a
1 b
2 b
3 a
4 a
5 a
6 a
dtype: object
正则替换需使用str.replace
逻辑替换包括了where和mask,这两个函数是完全对称的
where函数在传入条件为False的对应行进行替换,而mask在传入条件为True的对应行进行替换
当不指定替换行时,替换为缺失值
a = pd.Series([-1,1.2345,100,-50])
# where传入不小于0时,进行替换,不指定替换行时,替换为缺失值NaN
a.where(a<0)
0 -1.0
1 NaN
2 NaN
3 -50.0
dtype: float64
# mask传入值小于0时,进行替换,不指定替换行时,替换为缺失值NaN
a.mask(a<0)
0 NaN
1 1.2345
2 100.0000
3 NaN
dtype: float64
# mask传入值小于0时,进行替换,指定替换内容为-50,替换为-50.0000
a.mask(a<0, -50)
0 -50.0000
1 1.2345
2 100.0000
3 -50.0000
dtype: float64
# 传入的条件只需是与被调用的Series索引一直的布尔序列即可
# mask函数为True时,替换第二个参数的值
a_condition = pd.Series([True,False,False,True], index=a.index)
a.mask(a_condition, -50)
0 -50.0000
1 1.2345
2 100.0000
3 -50.0000
dtype: float64
数值替换包含了round,abs,clip方法,分别表示取整,取绝对值和截断
# round可以用参数指定保留小数点后几位数字
a =pd.Series([-1,1.2345,100,-50])
a.round(2)
0 -1.00
1 1.23
2 100.00
3 -50.00
dtype: float64
# abs()取绝对值
a.abs()
0 1.0000
1 1.2345
2 100.0000
3 50.0000
dtype: float64
# 两个数表示上下截断边界
# 如,第一个数-1.00,小于下边界数0,则返回下边界数0
# 第二个数在[0,2],所以保留原数
# 第三个数为100,高于上边界数2,则返回上边界数2
a.clip(0,2)
0 0.0000
1 1.2345
2 2.0000
3 0.0000
dtype: float64
2.3.5排序函数
1.值排序sort_values
2.索引排序sort_index
# set_index方法把年级和姓名两列作为索引
df_index = df[['Grade','Name','Height','Weight']].set_index(['Grade','Name'])
df_index
Height | Weight | ||
---|---|---|---|
Grade | Name | ||
Freshman | Gaopeng Yang | 158.9 | 46.0 |
Changqiang You | 166.5 | 70.0 | |
Senior | Mei Sun | 188.9 | 89.0 |
Sophomore | Xiaojuan Sun | NaN | 41.0 |
Gaojuan You | 174.0 | 74.0 | |
... | ... | ... | ... |
Junior | Xiaojuan Sun | 153.9 | 46.0 |
Senior | Li Zhao | 160.9 | 50.0 |
Chengqiang Chu | 153.9 | 45.0 | |
Chengmei Shen | 175.3 | 71.0 | |
Sophomore | Chunpeng Lv | 155.7 | 51.0 |
200 rows × 2 columns
# 对身高进行排序,默认参数ascending=True为升序
df_index.sort_values('Height').head()
Height | Weight | ||
---|---|---|---|
Grade | Name | ||
Junior | Xiaoli Chu | 145.4 | 34.0 |
Senior | Gaomei Lv | 147.3 | 34.0 |
Sophomore | Peng Han | 147.8 | 34.0 |
Senior | Changli Lv | 148.7 | 41.0 |
Sophomore | Changjuan You | 150.5 | 40.0 |
# 降序
df_index.sort_values('Height',ascending=False).head()
Height | Weight | ||
---|---|---|---|
Grade | Name | ||
Senior | Xiaoqiang Qin | 193.9 | 79.0 |
Mei Sun | 188.9 | 89.0 | |
Gaoli Zhao | 186.5 | 83.0 | |
Freshman | Qiang Han | 185.3 | 87.0 |
Senior | Qiang Zheng | 183.9 | 87.0 |
# 在体重相同的情况下,对身高进行排序,并且保持身高降序排列,体重升序排列
df_index.sort_values(['Weight','Height'],ascending=[True,False]).head()
Height | Weight | ||
---|---|---|---|
Grade | Name | ||
Sophomore | Peng Han | 147.8 | 34.0 |
Senior | Gaomei Lv | 147.3 | 34.0 |
Junior | Xiaoli Chu | 145.4 | 34.0 |
Sophomore | Qiang Zhou | 150.5 | 36.0 |
Freshman | Yanqiang Xu | 152.4 | 38.0 |
# 索引排序,元素的值在索引中,需要指定索引层的名字或者层号,用参数level表示
# 这里通过指定索引层的名字来表示,字符串的排列顺序是由字母顺序决定的
# 所以Grade是从F开始排序,Name是从Y开头的词排序
df_index.sort_index(level=['Grade','Name'],ascending=[True,False]).head()
Height | Weight | ||
---|---|---|---|
Grade | Name | ||
Freshman | Yanquan Wang | 163.5 | 55.0 |
Yanqiang Xu | 152.4 | 38.0 | |
Yanqiang Feng | 162.3 | 51.0 | |
Yanpeng Lv | NaN | 65.0 | |
Yanli Zhang | 165.1 | 52.0 |
# level用层号表示,同样的结果
df_index.sort_index(level=[0,1],ascending=[True,False]).head()
Height | Weight | ||
---|---|---|---|
Grade | Name | ||
Freshman | Yanquan Wang | 163.5 | 55.0 |
Yanqiang Xu | 152.4 | 38.0 | |
Yanqiang Feng | 162.3 | 51.0 | |
Yanpeng Lv | NaN | 65.0 | |
Yanli Zhang | 165.1 | 52.0 |
2.3.6 apply方法
apply常用于DataFrame的行迭代和列迭代
apply的参数往往是一个以序列为输入的函数
df_apply = df[['Height','Weight']]
def my_mean(x):
res = x.mean()
return res
# 对Height和Weight进行迭代
df_apply.apply(my_mean)
163.21803278688526
55.01587301587302
Height 163.218033
Weight 55.015873
dtype: float64
# 简单的函数可以转换成一行的隐形函数lambda表示
df_apply.apply(lambda x:x.mean())
Height 163.218033
Weight 55.015873
dtype: float64
# 指定axis=1,那么每次传入函数的就是行元素组成的Series
# 同逐行均值结果一致
df_apply.apply(lambda x:x.mean(),axis=1).head()
0 102.45
1 118.25
2 138.95
3 41.00
4 124.00
dtype: float64
# mad函数返回的是一个序列中偏离该序列均值的绝对值大小的均值
# 序列1,3,7,10中,均值为5.25,每一个元素偏离的绝对值为4.25,2.25,1.75,4.75,这个偏离序列的均值为3.25
# mad的解释示例:
df_apply.apply(lambda x:(x-x.mean()).abs().mean())
Height 6.707229
Weight 10.391870
dtype: float64
# mad直接调用方法,返回结果和上述一致
df_apply.mad()
Height 6.707229
Weight 10.391870
dtype: float64
note
使用pandas的内置函数处理和apply来处理同一个任务,速度会相差很多,所以要谨慎使用apply。2.4 窗口对象
pandas有3类窗口
滑动窗口rolling、扩张窗口expanding、指数加权窗口ewm要使用滑动窗口,就必须先要对一个序列使用.rolling得到滑窗对象
最重要的参数为窗口大小window
a=pd.Series([1,2,3,4,5])
roller=a.rolling(window=3)
roller
Rolling [window=3,center=False,axis=0]
得到滑窗对象后,能够使用相应的聚合函数进行计算
窗口包含当前行所在的元素
如,在第4个位置进行均值计算时,应当计算(2+3+4)/3,而不是(1+2+3)/3
# nan,nan,1->nan
# nan,1,2->nan
# 1,2,3->2.0
# 2,3,4->3.0
# 3,4,5->4.0
# 总结:有nan的时候返回nan
roller.mean()
0 NaN
1 NaN
2 2.0
3 3.0
4 4.0
dtype: float64
roller.sum()
0 NaN
1 NaN
2 6.0
3 9.0
4 12.0
dtype: float64
# 滑动相关系数的计算
s = pd.Series([1,2,6,16,30])
roller.cov(s)
0 NaN
1 NaN
2 2.5
3 7.0
4 12.0
dtype: float64
# 滑动协方差的计算
roller.corr(s)
0 NaN
1 NaN
2 0.944911
3 0.970725
4 0.995402
dtype: float64
# 支持使用apply传入自定义函数,其传入值是对应窗口的Series
roller.apply(lambda x:x.mean())
0 NaN
1 NaN
2 2.0
3 3.0
4 4.0
dtype: float64
shift,diff,pct_change是一组类滑窗函数,它们的公共参数为perids=n,默认为1,分别表示取向前第n个元素的值、与向前第n个元素做差(n阶差分)、与向前第n个元素相比计算增长率。这里的n可以为负,表示反方向的类似操作
a=pd.Series([1,3,6,10,15])
# 取向前第n个元素的值
# 1,3,6,10,15
# 每个位置向左数两个数:nan,nan,1,3,6
a.shift(2)
0 NaN
1 NaN
2 1.0
3 3.0
4 6.0
dtype: float64
# 与向前第n个元素做差(n阶差分)
# 1,3,6,10,15
# 1-nan->nan,3-nan->nan, 6-nan->nan, 10-1->9, 15-3->12
a.diff(3)
0 NaN
1 NaN
2 NaN
3 9.0
4 12.0
dtype: float64
# 与向前第n个元素相比计算增长率
# 1,3,6,10,15
# (1-nan)/nan->nan, (3-1)/1->2, (6-3)/3->1, (10-6)/6->0.66,(15-10)/6->0.5
a.pct_change()
0 NaN
1 2.000000
2 1.000000
3 0.666667
4 0.500000
dtype: float64
# 1,3,6,10,15
# 每个位置向后数一个数:3,6,10,15,nan
a.shift(-1)
0 3.0
1 6.0
2 10.0
3 15.0
4 NaN
dtype: float64
# 与向后第n个元素做差(n阶差分)
# 1,3,6,10,15
# 1-6->-5, 3-10->-7, 6-15->-9, 10-nan->nan, 15-nan->nan
a.diff(-2)
0 -5.0
1 -7.0
2 -9.0
3 NaN
4 NaN
dtype: float64
结合上面滑动窗口rolling,有没有发现它们的功能可以用窗口大小为n+1的rolling方法等价代替呢
# a.shift(2)
# 如果隐式函数不加list(x)[0],会报错cannot convert the series to
s.rolling(3).apply(lambda x:list(x)[0])
0 NaN
1 NaN
2 1.0
3 2.0
4 6.0
dtype: float64
# s.diff
s.rolling(4).apply(lambda x:list(x)[-1]-list(x)[0])
0 NaN
1 NaN
2 NaN
3 15.0
4 28.0
dtype: float64
# s.pct_change()
def my_pct(x):
L = list(x)
return L[-1]/L[0]-1
s.rolling(2).apply(my_pct)
0 NaN
1 1.000000
2 2.000000
3 1.666667
4 0.875000
dtype: float64
思考题:
rolling对象的默认窗口方向都是向前的,某些情况下用户需要向后的窗口,例如对 1,2,3设定向后窗口为 2 的 sum 操作,结果为 3,5,NaN,此时应该如何实现向后的滑窗操作?(提示:使用shift )
感谢群里大佬提供思路!
# 群里的思路
# 相当于当前值+后面的值
# 1+2->3, 2+3->5, 3+nan->nan
t=pd.Series([1,2,3])
t+t.shift(-1)
0 3.0
1 5.0
2 NaN
dtype: float64
# 群里提供的解法
# 3,2,1
# 3,2,1->nan,3,2
# 累加:nan,5,3
# 逆序 3,5,nan
(t[::-1]+t[::-1].shift(1))[::-1]
0 3.0
1 5.0
2 NaN
dtype: float64
2.4.2 扩张窗口
累计窗口,可以理解为一个动态长度的窗口,其窗口的大小就是从序列开始处,到具体操作的对应位置
其使用的聚合函数会作用于这些逐步扩张的窗口上
# 1->1,(1+3)/2->2,(1+3+6)/3>3.33,(1+3+6+10)/4->5
a = pd.Series([1,3,6,10])
a.expanding().mean()
0 1.000000
1 2.000000
2 3.333333
3 5.000000
dtype: float64
task01
df = pd.read_csv('pokemon.csv')
df.head(3)
# | 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 |
# 对HP,ATTACK,DEFENSE,SP,ATK,SP.Def,SPEED进行加总,验证是否为Total值
# 注意,用索引判断的话,需要将末尾列+1,相当于原本是5,6,7,8,9,10行,但是右括号是开区间,[5,11)
# 否则结果是1.0
# 用mean的话返回0.0,用max和min的话返回的是False
df = pd.read_csv('pokemon.csv')
(df[df.columns[5:11]].sum(axis=1) != df[df.columns[4]]).max()
False
# 答案
(df[['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed'
]].sum(1)!=df['Total']).mean()
0.0
对于# 重复的妖怪值保留第一条记录
2.a 求第一属性的种类数量和前三多数量对应的种类
df = pd.read_csv('pokemon.csv')
df['Type 1'].value_counts().head(3)
Water 112
Normal 98
Grass 70
Name: Type 1, dtype: int64
# 对于# 重复的妖怪值保留第一条记录
df_dup = df.drop_duplicates('#', keep='first')
df_dup
# | 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
# 求第一属性的种类数量
# nunique得到唯一值的个数
df_dup['Type 1'].nunique()
18
# 前三多数量对应的种类
df_dup['Type 1'].value_counts().head(3)
Water 105
Normal 93
Grass 66
Name: Type 1, dtype: int64
# 答案
df_dup['Type 1'].value_counts().index[:3]
Index(['Water', 'Normal', 'Grass'], dtype='object')
求第一属性和第二属性的组合种类
# shape获取序列的长度,.shape结果是(143,11),11是原本的列数,只需取143,即为行数
df_attr = df_dup.drop_duplicates(['Type 1','Type 2'])
df_attr.shape[0]
143
求尚未出现过的属性组合
我的思路:找到Type 1和Type 2的组合,将所有出现过Type 1和Type 2排列组合,然后将组合数减去已有的组合
# 答案给的思路
# 之后还要再想一想
pd_full = [' '.join([i,j]) if i!=j else i for j in df_dup['Type 1'].unique() for i in df_dup['Type 1'].unique()]
pd_part = [' '.join([i,j]) if type(j)!=float else i for i,j in zip(df_attr['Type 1'], df_attr['Type 2'])]
result = set(pd_full).difference(set(pd_part))
len(result)
181
取出物攻,超过120的替换为high,不足50的替换为low,否则为mid
df['Attack'].mask(df['Attack']>120,'high').mask(df['Attack']<50,'low').mask((df['Attack']>=50)&(df['Attack']<=120), '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
取出第一属性,分别用replace和apply替换所有字母为大写
# 换成大写查到了str.upper的用法
df['Type 1'].replace({i:str.upper(i) for i in df['Type 1']})
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
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
求每个妖怪六项能力的离差,即所有能力中偏离中位数最大的值,添加到df并从大到小排序
# 求所有能力中位数,变成array
# 遍历每一行,对每一行的每一个值和中位数做差,因为是求偏离的最大值,所以应该为差的绝对值
# apply第二个参数axis应设为1,以行遍历
df['Dev']=df[df.columns[5:11]].apply(lambda x: np.max((x-x.median()).abs()), axis=1)
df.sort_values('Dev',ascending=False)
# | Name | Type 1 | Type 2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Deviation | Dev | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
230 | 213 | Shuckle | Bug | Rock | 505 | 20 | 10 | 230 | 10 | 230 | 5 | 215.0 | 215.0 |
121 | 113 | Chansey | Normal | NaN | 450 | 250 | 5 | 5 | 35 | 105 | 50 | 207.5 | 207.5 |
261 | 242 | Blissey | Normal | NaN | 540 | 255 | 10 | 10 | 75 | 135 | 55 | 190.0 | 190.0 |
333 | 306 | AggronMega Aggron | Steel | NaN | 630 | 70 | 140 | 230 | 60 | 80 | 50 | 155.0 | 155.0 |
224 | 208 | SteelixMega Steelix | Steel | Ground | 610 | 75 | 125 | 230 | 55 | 95 | 30 | 145.0 | 145.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
143 | 132 | Ditto | Normal | NaN | 288 | 48 | 48 | 48 | 48 | 48 | 48 | 0.0 | 0.0 |
165 | 151 | Mew | Psychic | NaN | 600 | 100 | 100 | 100 | 100 | 100 | 100 | 0.0 | 0.0 |
255 | 236 | Tyrogue | Fighting | NaN | 210 | 35 | 35 | 35 | 35 | 35 | 35 | 0.0 | 0.0 |
206 | 191 | Sunkern | Grass | NaN | 180 | 30 | 30 | 30 | 30 | 30 | 30 | 0.0 | 0.0 |
271 | 251 | Celebi | Psychic | Grass | 600 | 100 | 100 | 100 | 100 | 100 | 100 | 0.0 | 0.0 |
800 rows × 13 columns
# 答案
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()
# | Name | Type 1 | Type 2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Deviation | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
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 |
2.5.2 指数加权窗口
# 题目
np.random.seed(0)
s = pd.Series(np.random.randint(-1,2,30).cumsum()).head()
s.ewm(alpha = 0.2).mean().head()
0 -1.000000
1 -1.000000
2 -1.409836
3 -1.609756
4 -1.725845
dtype: float64
# 套用公式
alpha = 0.2
s.expanding().apply(lambda x: ((1-alpha)**np.arange(x.shape[0])*x[::-1]).sum()
/ ((1-alpha) ** np.arange(shape[0])).sum())
0 -1.000000
1 -1.000000
2 -1.409836
3 -1.609756
4 -1.725845
dtype: float64
给定一个限制窗口n,只对包含自身最近的n个窗口进行滑动加权平均,给出新的wi和yt的更新公式,并通过rolling窗口实现这一功能
我的思路:和上一问一样,只是加了窗口限定的数值n,假设取n为3
alpha = 0.2
s.rolling(window=3).apply(lambda x: ((1-alpha)**np.arange(3)*x[::-1]).sum()
/ ((1-alpha) ** np.arange(3)).sum())
0 NaN
1 NaN
2 -1.409836
3 -1.737705
4 -2.000000
dtype: float64