Pandas从入门到精通(2)- Pandas基础知识梳理

开始学习之前,检查一下pandas的版本,要求1.1.4版之上

import numpy as np
import pandas as pd

for module in np,pd:
    print(module.__name__, module.__version__)
>>>
numpy 1.18.5
pandas 1.1.5

1. 文件读取

我们经常遇到的原始数据文件有csv/txt/excel等格式,pandas都有想对应的io接口,格式都一样,以csv文件为例,语法格式为:pd.read_csv(file_path)。如果文件在当前的环境目录中,直接写上文件名即可。

# 常用的数据格式有csv/txt/xls
df = pd.read_csv('job.csv')
df.head()

读取后一般都是使用head()函数查看前五行,结果如下:

image.png

点开这个函数,可以看到里面有很多可用的参数:


image.png

这里,比较常用的有:

  • header="infer"为默认,表示用数据的第0行作为列名。如果不想这样,可以传入header=None表示第一行不作为列名
  • sep = ',' : 分割参数,它使得用户可以自定义分割符号,一般是传入按照原始文档的分隔符
  • index_col表示把某一列或几列作为索引,索引的内容将会在下一个章节中详细阐述
  • usecols表示读取列的集合,默认读取所有的列,
  • nrows表示读取的数据行数,当一个数据集比较大,几十个G那种,一次性读取太费时,可以指定只读取前1000行等进行查看。这个功能在大数据竞赛中经常使用。

比如,在上面的例子中,我们将职位编号这个字段作为指定的index,同时只读取前10行:


image.png

2. 文件写入

当我们希望把处理之后的数据重新保存一份时,就需要将内存中的数据框写入文件。和读取类似,pandas的文件写入io同样支持很多种文件如csv、txt、excel、joson等格式。
语法格式为: df.to_csv(file_name, index = False)
需要注意的是一般是使用index = False,即不将index写入文件

3. pandas基本数据结构

Series和DataFrame是pandas中两种最基本和最重要的数据存储结构,其中:

  • Serise存储一列values
  • DataFrame存储多列values
    在某种意义上,可以认为DataFrame是由多个Series构成,也就是DataFrame种的任意一维都可以看成是一个单独的Series。下面通过代码来加深一下理解
data = {'city' : ['BJ', 'SH', 'GZ', 'SZ'],
        'rank': ['A+', 'A+', 'A', 'A-'],
        'whether': ['Bad', 'Bad','Good', 'Good' ]
       }
# 通过一个列表创建Series
s = pd.Series(data['city'])
print(type(s))
s
>>>

Out[12]:
0    BJ
1    SH
2    GZ
3    SZ
dtype: object

可以通过index和values属性查看Series相应的取值

# 查看Series的index和values
print(s.values)
print(s.index)
print('*' * 10)
s1 = pd.Series(data['city'], index = [i for i in 'ABCD'])
print(s1.values)
print(s1.index)
s1
>>>
['BJ' 'SH' 'GZ' 'SZ']
RangeIndex(start=0, stop=4, step=1)
**********
['BJ' 'SH' 'GZ' 'SZ']
Index(['A', 'B', 'C', 'D'], dtype='object')
Out[11]:
A    BJ
B    SH
C    GZ
D    SZ
dtype: object

从上面的代码可以看到创建Series时默认的index为从0开始的整数,但是也可以指定。
对于DataFrame也是同样的,创建的时候可以指定index


image.png

同样的可以使用 ”."操作符来访问其属性

print(type(df1.index), df1.index)
print(type(df1.columns),df1.columns)
print(type(df1.values), df1.values)
>>>
 Index(['row_0', 'row_1', 'row_2', 'row_3'], dtype='object')
 Index(['city', 'rank', 'whether'], dtype='object')
 [['BJ' 'A+' 'Bad']
 ['SH' 'A+' 'Bad']
 ['GZ' 'A' 'Good']
 ['SZ' 'A-' 'Good']]

可以看到DataFrame其内部的数据值是一个np.ndarray数据结构,即一个二维数组

4. 基本函数

  1. head(),tail()查看前/后n行
df = pd.read_csv('job.csv')
df.head().append(df.tail())

备注:可以使用append方法将head和tail拼接同时输出

image.png
  1. 基本信息查看:info/shape/describe等
print(df.shape) # 形状
print(df.columns) # 列名(字段)
print(df.info())
>>>
(975, 8)
Index(['职位编号', '职位名称', '薪资', '公司名称', '工作经验', '学历', '公司性质', '公司规模'], dtype='object')

RangeIndex: 975 entries, 0 to 974
Data columns (total 8 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   职位编号    975 non-null    int64 
 1   职位名称    975 non-null    object
 2   薪资      951 non-null    object
 3   公司名称    975 non-null    object
 4   工作经验    975 non-null    object
 5   学历      975 non-null    object
 6   公司性质    975 non-null    object
 7   公司规模    964 non-null    object
dtypes: int64(1), object(7)
memory usage: 61.1+ KB
None

注意,df.info(),info后面需要加(),该函数可以快速查看是否存在缺失值情况

  1. 唯一值函数
    对序列使用unique和nunique可以分别得到其唯一值组成的列表和唯一值的个数,若要统计每个取值的个数,则需要value_counts()函数
print(df['公司性质'].unique())
print('*' * 10)
print(df['公司性质'].nunique())
print('*' * 10)
print(df['公司性质'].value_counts())
>>>
['民营公司' '合资' '外资(欧美)' '外资(非欧美)' '国企' '上市公司' '事业单位' '创业公司']
**********
8
**********
民营公司       552
外资(欧美)     164
合资          98
外资(非欧美)     89
上市公司        39
国企          30
创业公司         2
事业单位         1
Name: 公司性质, dtype: int64
  1. 排序函数
    排序共有两种方式,其一为值排序,其二为索引排序,对应的函数是sort_values和sort_index。

5、窗口对象

pandas中有3类窗口,分别是滑动窗口rolling、扩张窗口expanding以及指数加权窗口ewm(Exponentially Weighted Moving)。需要说明的是,以日期偏置为窗口大小的滑动窗口将在第十章讨论,指数加权窗口见本章练习。

1. 滑窗对象

要使用滑窗函数,就必须先要对一个序列使用.rolling得到滑窗对象,其最重要的参数为窗口大小window

s = pd.Series([1,2,3,4,5])
roller = s.rolling(window = 3)
roller

Rolling [window=3,center=False,axis=0]

在得到了滑窗对象后,能够使用相应的聚合函数进行计算,需要注意的是窗口包含当前行所在的元素,例如在第四个位置进行均值运算时,应当计算(2+3+4)/3,而不是(1+2+3)/3:

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

对于滑动相关系数或滑动协方差的计算,可以如下写出:

s2 = pd.Series([1,2,6,16,30])
roller.cov(s2)

0     NaN
1     NaN
2     2.5
3     7.0
4    12.0
dtype: float64

roller.corr(s2)

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是一组类滑窗函数,它们的公共参数为periods=n,默认为1,分别表示取向前第n个元素的值、与向前第n个元素做差(与Numpy中不同,后者表示n阶差分)、与向前第n个元素相比计算增长率。这里的n可以为负,表示反方向的类似操作。

s = pd.Series([1,3,6,10,15])
s.shift(2)

0    NaN
1    NaN
2    1.0
3    3.0
4    6.0
dtype: float64

s.diff(3)

0     NaN
1     NaN
2     NaN
3     9.0
4    12.0
dtype: float64

s.pct_change()

0         NaN
1    2.000000
2    1.000000
3    0.666667
4    0.500000
dtype: float64

s.shift(-1)

0     3.0
1     6.0
2    10.0
3    15.0
4     NaN
dtype: float64

s.diff(-2)

0   -5.0
1   -7.0
2   -9.0
3    NaN
4    NaN
dtype: float64

作业于练习:

Ex1:口袋妖怪数据集

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

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

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

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

df = pd.read_csv('../data/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
  1. HP, Attack, Defense, Sp. Atk, Sp. Def, Speed进行加总,验证是否为Total值。

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

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

解答 :

  1. 取出对应columns的列 , 按列求和后与Total作不等于比较 , 结果应全为False
  • False的真值为0 , 求和后若值为0 , 则所有行加和都与Total相同 , 否则为True的个数 , 也是不等于Total的个数
  • 经验证 , 合为0 , 是Total
(df[df.columns[5:]].sum(1) != df['Total']).sum()

0

df_2 = df.drop_duplicates('#')
df_2['Type 1'].nunique()

18

df_2['Type 1'].value_counts()[:3]

Water     105
Normal     93
Grass      66
Name: Type 1, dtype: int64

  • Type 1Type 2两列合并去重 , 得到143种组合
df_2_2 = df_2.drop_duplicates(['Type 1','Type 2'])[['Type 1','Type 2']]
df_2_2.shape

(143, 2)

  • 分别对Type 1Type 2取唯一值后排列组合所有不同技能组合
  • 其中Type 1唯一值18种 , Type 2唯一值19种(含一个NaN) , 组合后的全部种类数为18*19-18=324
  • 将已经存在的143种组合与所有组合拼接去重且不保留重复值 , 剩下的就是没有出现过的组合 , 共324-143=181
all_com = pd.DataFrame([[i,j] for i in df_2['Type 1'].unique() for j in df_2['Type 2'].unique()if i!=j],columns=['Type 1','Type 2'])
pd.concat([all_com,df_2_2]).drop_duplicates(keep = False)

Type 1 Type 2
9 Grass Rock
10 Grass Water
11 Grass Electric
12 Grass Fire
13 Grass Dragon
... ... ...
318 Flying Fire
320 Flying Dark
321 Flying Ghost
322 Flying Bug
323 Flying Normal

181 rows × 2 columns

  • maskwhere都可以逻辑替换
a = df['Attack']
a.mask(a>120,'high').mask(a<50,'low').mask((a<=120)&(a>=50),'mid').head()

0    low
1    mid
2    mid
3    mid
4    mid
Name: Attack, dtype: object

  • type 1中每个唯一值映射
df['Type 1'].replace(df['Type 1'].unique(),[*map(lambda x:x.upper(),df['Type 1'].unique())]).head()

0    GRASS
1    GRASS
2    GRASS
3    GRASS
4     FIRE
Name: Type 1, dtype: object

df['Type 1'].apply(lambda x:x.upper()).head()

0    GRASS
1    GRASS
2    GRASS
3    GRASS
4     FIRE
Name: Type 1, dtype: object

  • 取出六项能力 , 按apply方法依次对每行求中位数 , 做差 , 绝对值 , 最大值 , 再排序
df['Deviation'] = df[df.columns[5:]].apply(lambda x:abs(x-x.median()).max(),1)
df['Deviation'].sort_values(ascending=False).head()

230    215.0
121    207.5
261    190.0
333    155.0
224    145.0
Name: Deviation, dtype: float64

Ex2:指数加权窗口

  1. 作为扩张窗口的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 = 0 i=0i=0表示当前元素,i = t i=ti=t表示序列的第一个元素。

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

y t = ∑ i = 0 t w i x t − i ∑ i = 0 t w i = x t + ( 1 − α ) x t − 1 + ( 1 − α ) 2 x t − 2 + . . . + ( 1 − α ) t x 0 1 + ( 1 − α ) + ( 1 − α ) 2 + . . . + ( 1 − α ) t 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}}yt​=∑i=0t​wi​∑i=0t​wi​xt−i​​=1+(1−α)+(1−α)2+...+(1−α)txt​+(1−α)xt−1​+(1−α)2xt−2​+...+(1−α)tx0​​

对于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个窗口进行滑动加权平滑。请根据滑窗函数,给出新的wiyt的更新公式,并通过rolling窗口实现这一功能。

My solution :
  1. expanding得到序列的扩张对象 , 为其加权即可 , 可以用np.average求加权平均数
  • 其中weight是一系列1 − α 1-\alpha1−α的整数幂 , 由于权重值随序列索引值增加而增加 , 1 − α < 0 1-\alpha<01−α<0 , 所以幂指数值应当随序列的索引值增加而减小
  • 幂指数值恰好与序列索引的逆序相同 , 因此有三种思路分别为 : 把序列索引值逆序后当成幂指数值 , 或按序列顺序值生成weight后再逆序 , 或直接把序列逆序 , 都可以得出加权平均结果
alpha = 0.2
s.expanding().apply(lambda x:np.average(x[::-1],weights = (1-alpha)**np.arange(len(x)))).head()

0   -1.000000
1   -1.000000
2   -1.409836
3   -1.609756
4   -1.725845
dtype: float64

  1. expanding得到的是扩张对象 , 序列长度呈阶梯式增加 , rolling窗口为固定长度的序列
  • 而指数加权方式不变 , 仅仅是需要加权的序列改变 , 因此只需将expanding改为rolling即可
  • wiyt的更新公式则分两种情况 , 设窗口为n , 当t < n时 , 序列长度不够 , 可以更新为NaN , 也可以用expanding更新
  • t >= n时 , 更新长度由t变为n , 更新后的yt为 :

y t = ∑ i = 0 n − 1 w i x t − i ∑ i = 0 n − 1 w i = x t + ( 1 − α ) x t − 1 + ( 1 − α ) 2 x t − 2 + . . . + ( 1 − α ) n − 1 x t − n + 1 1 + ( 1 − α ) + ( 1 − α ) 2 + . . . + ( 1 − α ) n − 1 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}}yt​=∑i=0n−1​wi​∑i=0n−1​wi​xt−i​​=1+(1−α)+(1−α)2+...+(1−α)n−1xt​+(1−α)xt−1​+(1−α)2xt−2​+...+(1−α)n−1xt−n+1​​

s.rolling(window=5).apply(lambda x: np.average(x[::-1],weights = (1-alpha)**np.arange(len(x)))).head()

0         NaN
1         NaN
2         NaN
3         NaN
4   -1.725845
dtype: float64

参考:开源内容Joyful Pandas, 作者 DataWhale耿远昊
另外,更多精彩内容也可以微信搜索,并关注公众号:‘Python数据科学家之路“ ,期待您的到来和我交流

你可能感兴趣的:(Pandas从入门到精通(2)- Pandas基础知识梳理)