【Task02】Pandas之基础

news/2024/11/25 18:34:47/

前言

本章的内容为Pandas基础

注,本次打卡所以用到的数据都放在了同级目录下的data文件夹中:

在这里插入图片描述

一、文件的读取和写入

1.文件读取

#读csv 逗号分隔值
df_csv = pd.read_csv('data/my_csv.csv')
print(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
#读txt
df_txt= pd.read_table('data/my_table.txt')
print(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
#读excel
df_excel= pd.read_excel('data/my_excel.xlsx')
print(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

安装2.0.1版本的xlrd会报错:
在这里插入图片描述

原因是最近xlrd更新到了2.0.1版本,只支持.xls文件。所以pandas.read_excel(‘xxx.xlsx’)会报错。

解决办法:

pip install xlrd==1.2.0

1)header参数的用法:

#读txt
df_txt= pd.read_table('data/my_table.txt')
print(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
# head参数使用 效果是在第一行填入新的索引
df_txt= pd.read_table('data/my_table.txt',header=None)
print(df_txt)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

2)index_col参数用法

#读csv 逗号分隔值
df_csv = pd.read_csv('data/my_csv.csv')
print(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
#读csv 逗号分隔值 使用index_col col3-5自动作为列索引?
df_csv = pd.read_csv('data/my_csv.csv',index_col=['col1','col2'])
print(df_csv)col3    col4      col5
col1 col2                        
2    a      1.4   apple  2020/1/1
3    b      3.4  banana  2020/1/2
6    c      2.5  orange  2020/1/5
5    d      3.2   lemon  2020/1/7

3)index_col参数用法

#读txt
df_txt= pd.read_table('data/my_table.txt')
print(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_txt= pd.read_table('data/my_table.txt',usecols=['col1','col2'])
print(df_txt)col1 col2
0     2    a
1     3    b
2     6    c
3     5    d

4)parse_dates参数用法

#读csv 逗号分隔值
df_csv = pd.read_csv('data/my_csv.csv')
print(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
#读csv 逗号分隔值
df_csv = pd.read_csv('data/my_csv.csv',parse_dates=['col5'])
print(df_csv)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

5)nrows参数用法

#读excel
df_excel= pd.read_excel('data/my_excel.xlsx')
print(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
#读excel 设定行数
df_excel= pd.read_excel('data/my_excel.xlsx',nrows=3)
print(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

6)sep参数用法

#直接读
df_txt = pd.read_table('data/my_table_special_sep.txt')
print(df_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
df_txt = pd.read_table('data/my_table_special_sep.txt',sep='\|\|\|\|',engine='python')
print(df_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?

2.数据写入

刚才我们读出来的DataFrame格式的数据相当于一份原文件的copy,当我们修改后,需要将它与原文件进行同步,我们以.xlsx文件为例,了解一下数据写入过程:

例一、2.1 修改excel中的列名

先记录一下原始文档的数据:
在这里插入图片描述

在这里插入图片描述

使用pip install openpyxl解决,注意这里不需要import新的包:

在这里插入图片描述

在这里插入图片描述
原因出在文件打开被占用

我们关掉刚才打开的excel文件后继续:

#读excel函数 打印并返回excel中的df数据
>>> def getExcel(excel_path):
>>>     df_excel= pd.read_excel(excel_path)
>>>     return df_excel
>>> excel_path = 'data/my_excel.xlsx'
>>> df_excel = getExcel(excel_path)
>>> print(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
#修改第一列列名
>>> df_excel.rename(columns={'col1':'hys'},inplace=True)
#df中数据
>>> print(df_excel)hys 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_excel.to_excel(excel_path,index=False)
#查看原文件
>>> print(getExcel(excel_path))hys 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

再查看excel的数据:

在这里插入图片描述

另说明,to_excel()方法中index的作用,如果设置成True(默认为True),会在df的基础上在最左侧加一列,比如:

在这里插入图片描述

显然这不是我们想要的,所以需要在无实际需要的时候,最好把index设置成False。

例一、2.2 将txt文件保存为csv文件并指定分隔符

#读txt
>>> df_txt= pd.read_table('data/my_table.txt')
>>> print(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_txt.to_csv('data/my_txt_20201219.txt',sep=',',index=False)

我们采用的分隔符是逗号,注意这里sep只能设置成一个字符,否则会报错:

在这里插入图片描述

查看文件:

在这里插入图片描述

例一、2.3 表格数据转成markdown格式

#读csv
>>> df_csv = pd.read_csv('data/my_csv.csv')
>>> print(df_csv.to_markdown())

产生报错:
在这里插入图片描述

通过pip install tabulate安装:

在这里插入图片描述

再次运行:

#读csv
>>> df_csv = pd.read_csv('data/my_csv.csv')
>>> res = df_csv.to_markdown()
>>> print(type(df_csv))
<class 'pandas.core.frame.DataFrame'>
>>> print(type(res))
<class 'str'>
>>> print(res)
|    |   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 |

可以看到,生成的markdown格式数据类型是 str 类型

我们来一个套娃测试,因为我现在编辑博客的格式就是markdown,把上面的输出放在博客里试一下:

col1col2col3col4col5
02a1.4apple2020/1/1
13b3.4banana2020/1/2
26c2.5orange2020/1/5
35d3.2lemon2020/1/7

验证成功。

例一、2.4 表格数据转成latex格式

在data文件夹下新建一个包含‘a+b=c’的csv文件:

在这里插入图片描述

#读csv
>>> df_csv = pd.read_csv('data/my_latex.csv')
>>> res = df_csv.to_latex()
>>> print(type(df_csv))
<class 'pandas.core.frame.DataFrame'>
>>> print(type(res))
<class 'str'>
>>> print(res)
\begin{tabular}{ll}
\toprule
Empty DataFrame
Columns: Index(['a+b = c'], dtype='object')
Index: Index([], dtype='object') \\
\bottomrule
\end{tabular}

TODO1:在Latex编辑器中验证。

二、基本数据结构

在上一章我们提到了,Pandas是基于Numpy构建的。Numpy有自己独特的ndarray数据类型,同样的,Pandas也有属于自己的两种基本的数据类型——SeriesDataFrame

下面让我们分别来介绍:

1.Series

class pandas.Series(data=None, index=None, dtype=None, name=None, copy=False, fastpath=False)

One-dimensional ndarray with axis labels

上面是官方文档对Series的介绍,翻译过来就是带有轴标签的一维ndarray

参数解释:

  • data:传入的数据
  • index:数据对应的标签
  • dtype:用作指定Series的输出数据类型,如果不指定会根据data去推断
  • name:Series的名字
  • copy:是否复制data
  • fastpath:是否快速精简模式

我们来举几个例子:

例二、1.1 创建Series数据

#定义data和index
>>> data = ['hys',25,'Beijing']
>>> index = ['name','age','loc']
#创建Series 方法一
>>> s = pd.Series(data,index,name='info')
#打印
print(s)
name        hys
age          25
loc     Beijing
Name: info, dtype: object
#创建Series 方法二
>>> s2 = pd.Series({'name':'hys','age': 25,'loc':'Beijing'},name='info')
>>> print(s2)
name        hys
age          25
loc     Beijing
Name: info, dtype: object

定义的s代表一个人的信息,其中包含3个标签(属性)。方法一是分别指定data和index来创建Series类型数据,而方法二是直接传入一个字典一并传入name和index。

Series常用属性:

属性说明
values获取数组
index获取索引
namevalues的name
index.name索引的name
dtype数据类型
shape数据的shape

Series类型数据的属性绝大部分都可以通过.属性名的方式来访问,下面举几个常用的属性:

例二、1.2 查看Series数据的属性

>>> s2 = pd.Series({'name':'hys','age': 25,'loc':'Beijing'},name='info')
>>> print(s2)
name        hys
age          25
loc     Beijing
Name: info, dtype: object#访问values属性
>>> print(s2.values)
['hys' 25 'Beijing']#访问index属性
>>> print(s2.index)
Index(['name', 'age', 'loc'], dtype='object')#访问dtype属性
>>> print(s2.dtype)
object#访问name属性
>>> print(s2.name)
info#访问shape属性
>>> print(s2.shape)
(3,)#通过index名来访问对应数据
>>> print(s2['name'])
hys

注意,在上面最后一个例子中我们使用了[]方式访问了Series数据指定index下的数据,它的调用方式类似于Python中的字典:

>>> infoDict = {'name':'hys','age': 25,'loc':'Beijing'}
>>> print(infoDict['loc'])
Beijing

与ndarray相似,Series数据类型中也有提前定义好的方法供我们使用,我们在这不进行单独讲解,会在本文第三节中一并讲解Series和DataFrame中常用的方法。

2.DataFrame

class pandas.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)

Two-dimensional, size-mutable, potentially heterogeneous tabular data.

官方对DataFrame的解释是,二维大小可变的潜在异构表格数据。

相比于Series,DataFrame中的参数少了一个name和fastpath,多了一个columns,columns是列标签的意思。

举例说明:

例二、2.1 创建DataFrame数据

>>> data = [['hys',25,'Beijing'],['xmy',25,'Shanghai']]
>>> index = ['01','02']
>>> columns = ['name','age','loc']
#创建DataFrame 方法一
>>> df = pd.DataFrame(data,index,columns)
>>> print(df)name  age       loc
01  hys   25   Beijing
02  xmy   25  Shanghai
#创建DataFrame 方法二 
>>> dataDict = {'name':['hys','xmy'],'age':[25,25],'loc':['Beijing','Shanghai']}
>>> df2 = pd.DataFrame(dataDict)
>>> print(df2)name  age       loc
0  hys   25   Beijing
1  xmy   25  Shanghai

同样地,我们也可以利用两种方式创建DataFrame型数据,上面的方法一仍然是常规方法,index和columns分别代表视觉上的行索引和列索引。方法二依然采用字典方式构建,其中字典的键名代表columns,index默认为0,1,…直到任意键值列表的长度-1。

同样地,我们也可以利用.属性名、[行索引名]、[列索引名]去访问指定的属性:

例二、2.2 查看DataFrame数据的属性

>>> dataDict = {'name':['hys','xmy'],'age':[25,25],'loc':['Beijing','Shanghai']}
>>> df2 = pd.DataFrame(dataDict)
>>> print(df2)name  age       loc
0  hys   25   Beijing
1  xmy   25  Shanghai#访问values属性
>>> print(df2.values)
[['hys' 25 'Beijing']['xmy' 25 'Shanghai']]#访问index属性
>>> print(df2.index)
RangeIndex(start=0, stop=2, step=1)#访问culumns属性
>>> print(df2.columns)
Index(['name', 'age', 'loc'], dtype='object')#访问dtypes属性
>>> print(df2.dtypes)
name    object
age      int64
loc     object
dtype: object#访问shape属性
>>> print(df2.shape)
(2, 3)#通过columns名来访问对应数据
>>> print(df2['name'],type(df2['name']))
0    hys
1    xmy
Name: name, dtype: object <class 'pandas.core.series.Series'>

这里要注意DataFrame数据是没有dtype属性的,取而代之的是dtypes属性,它返回的是每一行单独的dtype和整个DataFrame整体的dtype。

另外值得注意的一点是,型如df2['name']的列访问得到的数据类型是Series.

三、常用基本函数

由于从某种程度上可以把Series数据当做DataFrame数据的子集,所以在本节我们以DataFrame为主介绍pandas数据类型的常用基本函数。

采用的数据集来自data文件夹下的learn_pandas.csv:

在这里插入图片描述
利用上一节的知识简单查看一下数据集的重要属性:

#读入数据
>>> df_data = pd.read_csv('./data/learn_pandas.csv')
#查看数据的shape
>>> print(df_data.shape)
(200, 10)#查看列标签
>>> print(df_data.columns)
Index(['School', 'Grade', 'Name', 'Gender', 'Height', 'Weight', 'Transfer','Test_Number', 'Test_Date', 'Time_Record'],dtype='object')

可以看到,所用数据集的shape为(200,10),共10个列标签,且没有设置行标签。

1.汇总函数

DataFrame.head(n=5)
DataFrame.tail(n=5)

head()方法tail()方法分别返回前n行和后n行的数据,默认n=5:

例三、1.1 按行返回DataFrame数据

>>> df_data = pd.read_csv('./data/learn_pandas.csv')
>>> df_data = df_data[df_data.columns[:7]]
>>> print(df_data.head())
>>> print(df_data.tail())

在这里插入图片描述

例三、1.2 返回DataFrame数据信息概括和列统计量

>>> df_data = pd.read_csv('./data/learn_pandas.csv')
>>> df_data = df_data[df_data.columns[:7]]
>>> print(df_data.info())
<class 'pandas.core.frame.DataFrame'>
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    float645   Weight    189 non-null    float646   Transfer  188 non-null    object 
dtypes: float64(2), object(5)
memory usage: 11.1+ KB
None
>>> print(df_data.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

2.特征统计函数

如果不想返回describe()方法返回的全部统计量,我们可以使用单独的统计方法进行计算,默认为按列统计(axis=0),也可以指定axis的值:

>>> df_data = pd.read_csv('./data/learn_pandas.csv')
#选取身高和体重列
>>> df_data = df_data[['Height','Weight']]
#统计非缺失值个数
>>> print(df_data.count())
Height    183
Weight    189
dtype: int64#计算均值
>>> print(df_data.mean())
Height    163.218033
Weight     55.015873
dtype: float64#计算标准差
>>> print(df_data.std())
Height     8.608879
Weight    12.824294
dtype: float64#最小值
>>> print(df_data.min())
Height    145.4
Weight     34.0
dtype: float64#最大值
>>> print(df_data.max())
Height    193.9
Weight     89.0
dtype: float64#四分位数 25%和75%百分位
>>> print(df_data.quantile(0.25))
Height    157.15
Weight     46.00
Name: 0.25, dtype: float64
>>> print(df_data.quantile(0.75))
Height    167.5
Weight     65.0
Name: 0.75, dtype: float64#返回最大最小值的行索引
>>> print(df_data.idxmax())
Height    193
Weight      2
dtype: int64
>>> print(df_data.idxmin())
Height    143
Weight     49
dtype: int64

3.唯一值函数

unique()方法可以获得由唯一值组成的数组,nunique()方法返回的是唯一值的个数:

>>> df_data_school = pd.read_csv('./data/learn_pandas.csv')['School']
>>> print(df_data_school.unique())
['Shanghai Jiao Tong University' 'Peking University' 'Fudan University''Tsinghua University']
>>> print(df_data_school.nunique())
4
>>> print(len(df_data_school.unique()))
4

不难看出,x.nunique()len(x.unique())是等价的。

value_counts()方法可以查看每个唯一值对应出现的次数:

>>> print(df_data_school.value_counts())
Tsinghua University              69
Shanghai Jiao Tong University    57
Fudan University                 40
Peking University                34
Name: School, dtype: int64
>>> df_data = pd.read_csv('./data/learn_pandas.csv')
>>> df_data = df_data[['Gender','Transfer','Name']]
>>> print(df_data.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

默认drop_duplicates()方法参数keep为first,代表展示第一次出现的行,值为last表示展示最后一次出现的行。

易知这两种情况返回的行数为每列属性种类的累积

duplicated()方法返回的是对应列的值是否重复,类型为布尔类型:

>>> df_data = pd.read_csv('./data/learn_pandas.csv')
>>> df_data = df_data[['Gender','Transfer','Name']]
>>> print(df_data.duplicated(['Gender', 'Transfer']).head(6))
0    False
1    False
2     True
3     True
4     True
5     True
dtype: bool

返回结果代表的是当前行的’Gender’和’Transfer’的组合是否从未出现过。

4.替换函数

1)映射替换

replace()方法可以用于映射替换:

>>> df_data = pd.read_csv('./data/learn_pandas.csv')
>>> replace_dict = {'Male':'男','Female':'女'}
>>> print(df_data['Gender'].replace(replace_dict).head(6))
012345    女
Name: Gender, dtype: object

我们还可以通过锁定替换的值后设定replace的method参数指定替换对齐方式,‘ffill’代表‘forward’向前,‘bfill’代表backward向后:

>>> data = pd.Series(np.random.randint(0,3,5))
>>> print(data)
0    0
1    2
2    0
3    2
4    1
dtype: int32
>>> print(data.replace([1],method='ffill'))
0    0
1    2
2    0
3    2
4    2
dtype: int32
>>> print(data.replace([1],method='bfill'))
0    0
1    2
2    0
3    2
4    1
dtype: int32

我们可以看到,当被替换值处于边界状态下,后面无值可替,它会保持不变。

2)逻辑替换

where()方法和mask()方法的作用正好是相反的,前者替换不满足条件的值,后者替换满足条件的值,默认将被替换值变为NaN类型:

>>> data = pd.Series(np.random.randint(0,3,5))
>>> print(data)
0    0
1    1
2    0
3    0
4    0
dtype: int32
>>> print(data .where(data<1))
0    0.0
1    NaN
2    0.0
3    0.0
4    0.0
dtype: float64
>>> print(data .mask(data<1))
0    NaN
1    1.0
2    NaN
3    NaN
4    NaN
dtype: float64

观察到一个小小的细节,就是这两种方法都使原始数据类型从int32变为了float64。

3)数值替换

这里介绍3个方法:round()abs()clip(),分别代表取整、求绝对值和截断:

>>> data = pd.Series([1.5,-1.6,1.4,-1.3])
>>> print(data)
0    1.5
1   -1.6
2    1.4
3   -1.3
dtype: float64
>>> print(data.round(0))
0    2.0
1   -2.0
2    1.0
3   -1.0
dtype: float64
>>> print(data.abs())
0    1.5
1    1.6
2    1.4
3    1.3
dtype: float64
>>> print(data.clip(-1,1))
0    1.0
1   -1.0
2    1.0
3   -1.0
dtype: float64

我们可以注意到,round()方法采用的是四舍五入原则。

练一练

题目:在 clip 中,超过边界的只能截断为边界值,如果要把超出边界的替换为自定义的值,应当如何做?

超出边界的值替换成指定的值:

思路:利用where进行多条件判断,并指定替换值

>>> data = pd.Series([1.5,-1.6,1.4,-1.3])
>>> print(data)
>>> print(data.where((data>-1) & (data <2),100))
0    1.5
1   -1.6
2    1.4
3   -1.3
dtype: float64
0      1.5
1    100.0
2      1.4
3    100.0
dtype: float64

5.排序函数

1)值排序

>>> df_data = pd.read_csv('./data/learn_pandas.csv')
>>> df_data = df_data[['Grade', 'Name', 'Height', 'Weight']].set_index(['Grade','Name'])
#默认为True 代表升序排列
>>> print(df_data.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#False代表升序排列
>>> print(df_data.sort_values('Height',ascending=False).head())Height  Weight
Grade    Name                         
Senior   Xiaoqiang Qin   193.9    79.0Mei Sun         188.9    89.0Gaoli Zhao      186.5    83.0
Freshman Qiang Han       185.3    87.0
Senior   Qiang Zheng     183.9    87.0

如果指定了两个列,那么在身高相同时,默认两列均使用升序排列。

2)索引排序

索引排序需要在列名前指定level,否则会报错:

在这里插入图片描述

>>> df_data = pd.read_csv('./data/learn_pandas.csv')
>>> df_data = df_data[['Grade', 'Name', 'Height', 'Weight']].set_index(['Grade','Name'])
#默认为True 代表升序排列
>>> print(df_data.sort_index(level=['Grade','Name'],ascending=[True,False]).head())Height  Weight
Grade    Name                         
Freshman Yanquan Wang    163.5    55.0Yanqiang Xu     152.4    38.0Yanqiang Feng   162.3    51.0Yanpeng Lv        NaN    65.0Yanli Zhang     165.1    52.0

这里的升降序指得是字典顺序。

6.apply方法

apply()用于用指定的自定义函数去计算该数据,如:

>>> def func(x):
>>>     return x.max()
>>> df_data = pd.read_csv('./data/learn_pandas.csv')
>>> df_data = df_data[['Height','Weight']].head()
>>> print(df_data.apply(func))
Height    188.9
Weight     89.0
dtype: float64

当然此时可以利用拉姆达函数去省去def,但效果不如直接使用内置函数:

在这里插入图片描述

四、窗口对象

1.滑窗对象

rolling()方法为例举例说明:

>>> data = pd.Series(np.arange(10))
>>> roller = data.rolling(window=4)
>>> roller.mean()
0    NaN
1    NaN
2    NaN
3    1.5
4    2.5
5    3.5
6    4.5
7    5.5
8    6.5
9    7.5
dtype: float64

窗口默认为窗口的最后一个单元先放到数据的第一个位置,然后一直滑动到最后一个位置所组成的窗口位置集合,长度等于数据的长度,值为NaN的行有数据长度-1个。

滑动窗口支持传入自定义函数,如:

>>> data = pd.Series(np.arange(10))
>>> roller = data.rolling(window=4)
>>> print(roller.apply(lambda x:x.min()))
0    NaN
1    NaN
2    NaN
3    0.0
4    1.0
5    2.0
6    3.0
7    4.0
8    5.0
9    6.0
dtype: float64

shift()方法表示将数据按指定方向移动,比如:

>>> data = pd.Series(np.arange(10))
>>> print(data)
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int32
>>> data.shift(5)
0    NaN
1    NaN
2    NaN
3    NaN
4    NaN
5    0.0
6    1.0
7    2.0
8    3.0
9    4.0
dtype: float64

即表示数据向下移动5个单位,空缺用NaN补全,并且转换了dtype。如果实参为负数,则表示向下移动,移动距离为负数的绝对值。

diff()方法表示数据的差值,如:

>>> data = pd.Series(np.arange(10))
>>> print(data)
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int32
>>> print(data.diff(2))
0    NaN
1    NaN
2    2.0
3    2.0
4    2.0
5    2.0
6    2.0
7    2.0
8    2.0
9    2.0
dtype: float64

上面的例子表示,每个数据减去其上面2个位置的数据得到的如果,如果值不存在则填NaN。注意,如果不指明位置,默认为1,即上面相邻的数据。如果实参为负数,同上。

pct_change()方法表示计算数据的相对增长率,如不输入实参,则默认将相邻的上方数据作为参考指标进行计算:

>>> data = pd.Series(np.arange(10))
>>> print(data)
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int32
>>> print(data.pct_change())
0         NaN
1         inf
2    1.000000
3    0.500000
4    0.333333
5    0.250000
6    0.200000
7    0.166667
8    0.142857
9    0.125000
dtype: float64

练一练

题目:rolling对象的默认窗口方向都是向前的,某些情况下用户需要向后的窗口,例如对1,2,3设定向后窗口为2的sum操作,结果为3,5,NaN,此时应该如何实现向后的滑窗操作?

2.扩张窗口

>>> data = pd.Series(np.arange(10))
>>> print(data.expanding().mean())
0    0.0
1    0.5
2    1.0
3    1.5
4    2.0
5    2.5
6    3.0
7    3.5
8    4.0
9    4.5
dtype: float64

扩张窗口等价与把滑动窗口值为Nan的位置补全为0,相当于在左侧延长n-1长度的0(n为数据长度)

练一练

题目:cummax, cumsum, cumprod函数是典型的类扩张窗口函数,请使用expanding对象依次实现它们。

>>> s = pd.Series([1,3,6,10])
#cummax
>>> print(s.expanding().max())
0     1.0
1     3.0
2     6.0
3    10.0
dtype: float64
#cumsum
>>> print(s.expanding().sum())
0     1.0
1     4.0
2    10.0
3    20.0
dtype: float64
#cumprod
>>> print(s.expanding().apply(lambda x:x.prod()))
0      1.0
1      3.0
2     18.0
3    180.0
dtype: float64

前两个实现起来非常简单,最后一个值得思考,因为下面这种方式是不行的,只好用apply()方法:

在这里插入图片描述

五、练习

Ex1:口袋妖怪数据集

>>> data = pd.read_csv('data/pokemon.csv')
>>> data.head()

在这里插入图片描述

1.验证Total值是否准确

>>> data = pd.read_csv('data/pokemon.csv')
>>> total = data['Total']
>>> data_six = data[data.columns[5:]]
>>> total_calc = data_six.sum(axis=1)
>>> print(np.all(total==total_calc))
True

思路:先把Total和其余6个属性值分堆处理,存放成Series数据,最后利用Numpy的all()方法进行比较。

2.“#”属性重复的妖怪只保留第一条记录

>>> data = pd.read_csv('data/pokemon.csv')
>>> data = data.drop_duplicates('#',keep='first')
>>> print(data.shape)
(721, 11)

先删掉#重复的妖怪。

1)求第一属性的种类数量和前三多数量对应的种类

>>> Type1 = data['Type 1']
>>> print(Type1.nunique())
18
>>> print(Type1.value_counts()[:3].index.values)
['Water' 'Normal' 'Grass']

思路:利用nunique()方法求出第一属性种类数量,利用value_counts()方法求出数量降序排列的第一属性种类名,然后通过索引和index()方法取得Index对象,最终通过.values属性拿到List类型的数据。

2)求第一属性和第二属性的组合种类

>>> res = data.drop_duplicates(['Type 1','Type 2'])
>>> print(res.shape)
(143, 11)

思路:利用drop_duplicates()方法求出包含第一和第二属性无重复的组合种类。

3)求尚未出现过的属性组合

#返回指定列名的无重复数据列表
#ndarray格式 求得所有类别 
>>> nd_types = data['Type 1'].unique()
>>> print(nd_types.shape)
(18,)
#已知总共18个属性 共18*18=324个组合种类 名称存放在名为total_types的list中
>>> total_types = [i+'_'+j if i!=j else i for i in nd_types for j in nd_types]
#获取现有的组合种类数据
>>> s_nowTypes = data.drop_duplicates(['Type 1','Type 2'])
>>> print(s_nowTypes.shape)
(143, 11)
>>> def func(x):
>>>     if type(x[3])==str:
>>>         return x[2]+'_'+x[3]
>>>     else:
>>>         return x[2]
#利用func函数,过滤无第二属性的妖怪,并存入now_types的Series数据类型中
>>> now_types = s_nowTypes.apply(func,axis=1)
#列表推导表达式获得结果
>>> res = [x for x in total_types if x not in now_types.values]
>>> print(len(res))
181

思路:
1)先求出所有属性,共18种,然后组合种类得到324种,注意两种一样的属性不要设成“x_x”,而直接叫x,将全部种类名存入名为total_types的List中。
2)然后根据上一题,找到当前已有的种类,此处利用apply()方法并将axis设为1即为按行遍历,注意此时data的列长仍为11,所以需要判断第4列的type2是否为NaN,等价于判断否是为字符,如果是字符的话说明是双属性妖怪,返回拼接后的结果,否则只返回type1。将生成的数据存到名为now_types的Series中。
3)利用列表推导表达式判断尚未出现过的属性组合。

另外,分析一下对怪物数据的理解,首先假设data代表全部800条数据,其中存在重复‘#’值的情况,这代表不同怪物的不同形态,比如:

在这里插入图片描述
代欧奇希斯,拥有四个战斗形态分别是,一般形态、攻击形态、防御形态与速度形态,根据形态不同它的各项指标会大幅度变化。

我们通过data1 = data.drop_duplicates('#',keep='first')只取同一个怪物的不同形态的第一种,data1中含有731条数据。然后通过data2 = data.drop_duplicates(['Type 1','Type 2'])只取相同属性组合的第一种,data2中含有143条数据,代表目前存在的属性组合。因为怪物不存在两个属性均为一个的情况,所以单属性的怪物可以被双属性相同的怪物代表,即属性组合一共18*18=324种,减去data2的数据量,即324-143=181种。

更进一步,我们可以做出属性组合矩阵的可视化呈现:

#组合种类矩阵 行索引表示第一属性 列索引表示第二属性
>>> df_types = pd.DataFrame(index=types,columns=types)
#单次增加不同种类怪物数量
>>> def add(index1,index2):
>>>     value = df_types[index1][index2]#如果当前统计值为NaN
>>>     if type(value)==float:
>>>         df_types[index1][index2] = 1
>>>     else:
>>>         df_types[index1][index2] += 1
>>> def func(x):#这里调换了i,j的顺序,为了适应df先列后行的访问次序 如df_types['Grass']['Fire']=0 访问的是'Grass'列'Fire'行#i为type2属性
>>>     i = x[3]#j为type1属性
>>>     j = x[2]#如果type2为nan
>>>     if type(i)==float:#则将type2赋值为type1
>>>         i = j#调用函数
>>>     add(i,j)
>>> data = pd.read_csv('data/pokemon.csv')
>>> data = data.drop_duplicates('#',keep='first')
>>> data.apply(func,axis=1)
>>> print(df_types.count().sum())
143
>>> df_types

在这里插入图片描述
df_types存放的是不同属性组合,有了这个DataFrame数据,我们就可以轻易获得有关组合属性的相关数据,比如获得单属性怪物的数量:

#求矩阵的迹 实际意义为单属性怪物的数量
>>> df_types.values.trace()
371

3.按要求构造Series

a.生成包含物攻的Series并按要求替换

>>> data = pd.read_csv('data/pokemon.csv')
>>> data_attack = data['Attack']
>>> data_attack.mask((data_attack>=50) & (data_attack <=120),100,inplace=True)
>>> data_attack.mask((data_attack>120),150,inplace=True)
>>> data_attack.mask((data_attack<50),0,inplace=True)
>>> data_attack.replace({0:'low',100:'mid',150:'high'},inplace=True)
>>> print(data_attack)
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
>>> print(data_attack.unique())
['low' 'mid' 'high']

思路:第一步用mask()方法做一个等价替换,把三个区域的值都用一个数字表示;第二部用replace()方法的映射替换。

如果省去第二部直接替换,在执行第二个mask()的时候,会出现字符(mid)和数字进行比较,无法顺利实现,所以采用分阶段的方式。

b.生成包含第一属性的Series并转为大写字母

>>> data = pd.read_csv('data/pokemon.csv')
#方法一 直接转换
>>> data_type1 = data['Type 1']
>>> data_type1 = data['Type 1'].str.upper()
>>> print(data_type1)
#方法二 使用apply转换
>>> data_type1 = data['Type 1']
>>> data_type1 = data_type1.apply(lambda x:str.upper(x))
>>> print(data_type1)
#方法三 使用replace转换
>>> data_type1 = data['Type 1']
>>> replace_Dict = {x : str.upper(x) for x in data_type1.unique() }
>>> data_type1.replace(replace_Dict,inplace=True)
>>> print(data_type1)
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.生成每个妖怪的离差并从大到小排序

#作用求离差
>>> def getDev(x):
>>>     return ((x-x.median()).abs()).max()
>>> data = pd.read_csv('data/pokemon.csv')
#data表示所有能力值
>>> data = data[data.columns[-6:]]
>>> res = data.apply(getDev,axis=1).sort_values(ascending=False)
>>> print(res)
261    190.0
121    185.0
224    160.0
230    160.0
333    160.0...  
175    -25.0
732    -27.0
446    -28.0
255    -30.0
206    -35.0
Length: 800, dtype: float64

思路:对整个DataFrame使用apply()方法,其中自定义函数getDev()的作用是返回离差,然后对离差进行降序排列(ascending设为False)。

仔细解释一下getDev()函数,首先在调用函数时,指定axis为1,即按行运算,然后x代表着每行的数据,属于Series类型。因为Series依然可以进行向量化运算,所以x-x.median()生成的是六项能力与中位数的差值的Series类型数据,然后利用max()方法求得这个妖怪的离差并返回。

Ex2:指数加权窗口

1.expanding窗口实现ewm

#自定义方法
>>> def my_ewm(s,alpha):
>>>     def func(x):
>>>         s_deno = (1-alpha)**np.arange(x.size-1,-1,-1)
>>>         return (s_deno*x).sum()/s_deno.sum()
>>>     return s.expanding().apply(func)
>>> np.random.seed(20201219)
>>> s  = pd.Series(np.random.randint(-1,2,30).cumsum())
>>> alpha = 0.2
#系统方法
>>> print(s.ewm(alpha=alpha).mean().head())
0    0.000000
1    0.555556
2    1.147541
3    1.436314
4    1.603998
dtype: float64
#自定义方法
>>> print(my_ewm(s,alpha).head())
0    0.000000
1    0.555556
2    1.147541
3    1.436314
4    1.603998
dtype: float64

思路:从expanding()方法入手,对于每一个扩张窗口,第一个窗口值的(1-α)的次数为0,然后依次递增,且数目和窗口宽度相同,反过来说,由加权归一化公式可知窗口的第一个值是与最大幂数的(1-α)相乘,因此考虑先建立一个ndarray数组用于存储倒序的幂数数组,首项为窗口大小-1,长度为窗口大小。然后利用Numpy的向量化性质进行公式复现即可。

注意np.arange(10,-1,-1)生成的是以10开始,0结束的递减ndarray数组。

2.给定限制窗口n,给出公式及实现

新的权重没有发生变化,但是 y t y{_t} yt的公式中求和公式的上限变为n-1:

在这里插入图片描述

区别在于限制了窗口大小,所以我们在上一题设计的幂数数组的长度也会固定,实现方法为:

#自定义方法
>>> def my_new_ewm(s,alpha,n):
>>>     def func(x):
>>>         return (s_deno*x).sum()/s_deno.sum()
>>>		s_deno = (1-alpha)**np.arange(n-1,-1,-1)
>>>     return s.rolling(window=n).apply(func)
>>> np.random.seed(20201219)
>>> s  = pd.Series(np.random.randint(-1,2,30).cumsum())
>>> alpha = 0.2
>>> n=4
#自定义方法
>>> print(my_new_ewm(s,alpha,n))
0         NaN
1         NaN
2         NaN
3    1.436314
4    1.826558
dtype: float64

实现方法的改动在于函数参数新增了一个窗口宽度n,另外由于窗口保持固定,所以s_deno数组只需生成一次即可,自定义函数func()没有本质的变化。

参考文献

#pandas DataFrame的修改方法
1.https://www.cnblogs.com/datasnail/p/9787808.html

#pandas官方API
2.https://pandas.pydata.org/pandas-docs/stable


http://www.ppmy.cn/news/415831.html

相关文章

python 宝可梦_python学习笔记——保可梦数据分析,Python4,宝可梦

运用数据分析的方式来了解宝可梦这种神奇的生物 首先进行数据集下载 # 数据集下载 !wget -O pokemon_data.csv https://pai-public-data.oss-cn-beijing.aliyuncs.com/pokemon/pokemon.csv 数据集下载完成后我们就要import我们最常用的三大件&#xff1a;Pandas, Seaborn, Matp…

python 加一个月 日期,有没有一种简单的方法可以在Python中将datetime对象增加一个月?...

本问题已经有最佳答案,请猛点这里访问。 所以我试图找到一种方法,将日期时间对象增加一个月。然而,根据这个问题,这似乎不是那么简单。 我希望有这样的事情: import datetime as dt now = dt.datetime.now() later = now + dt.timedelta(months=1) 但那不管用。如果可能的…

阿里云天池task4学习笔记

一、具体的学习内容 0、研究背景 数据时代的到来刷新了人们探索未知的方式&#xff0c;从基础能源建设到航天航空工程。在关都地区真新镇大木研究所一直孜孜不倦对精灵宝可梦进行研究的大木博士也不例外&#xff0c;在剧中我们就常常可以看到大木博士制作的精灵图鉴一直在给探险…

❤️手把手教你配置服务器板载raid❤️

❤️手把手教你配置服务器板载raid❤️ 当我们买的服务器没有raid卡该怎么办&#xff1f; 但是我们还想给服务器系统做个冗余盘该怎么办&#xff1f; 接下来请去机房带上显示器键盘&#xff0c;开始操作吧&#xff01;&#xff01;&#xff01; 现在以浪潮服务器NF5270M5、NF…

欧奇 A12 root教程_方法

欧奇 A12的root教程在这里整理了一下&#xff0c;之前有机友说自己的手机想删除系统自带的一些无用软件&#xff0c;可是怎么也删除不了&#xff0c;所以需要先进行root才可以删除&#xff0c;不然的话是 删除不了的&#xff0c;这个方法也是大家在root过程中总结出来了&#x…

激光盐密灰密测试仪

一、产品特点 KDYM-302L 激光盐密灰密测试仪采用检测技术将灰密测试与盐密测试合二为一&#xff0c;可同时检测出被测绝缘子的灰密度和盐密度&#xff0c;简化了绝缘子污秽检测的流程&#xff0c;非常适合在巡检现场和实验室使用。 二、主要特点 内置测试专用测试软件&#x…

2.0C++继承

C继承概述 C 中的继承是指一个类可以从另一个类中继承属性和方法&#xff0c;这个被继承的类称为基类或父类&#xff0c;继承它的类称为派生类或子类。 C三种继承 1、公有继承 public 在公有继承中&#xff0c;基类的公有成员和保护成员都可以被派生类访问&#xff0c;而基…

指定数组的维度,返回由随机数构成的数组numpy.random.ranf()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 指定数组的维度&#xff0c; 返回由随机数构成的数组 numpy.random.ranf() 选择题 以下说法错误的是? import numpy as np print("【执行1】np.random.ranf()") print(np.random.ra…