放一个目录做的的思维导图

一场数模国赛下来发现对于pandas里的一些基础操作还不是很熟练,整个建模过程中用到了许多pandas中对DataFrame的索引,切片访问,分组交叉透视等功能,除此以外稍微高级一点的就是用了几次apply函数来对某一行的值进行一个计算输出,虽然知识描述统计这部分的内容,但是能明显的感觉到对于这些基本功能有些生疏。坦白来讲,pandas的描述统计相关的这些工作其实借助excel也能够实现,但是当考虑到可迁移性这些方面的内容时,使用编程语言的优越性也就自然而然地体现出来,当然,如果对相关的函数不能做到很熟悉的话,其实反而加大了工作量。

简介

Pandas 是 Python 的核心数据分析支持库,提供了快速、灵活、明确的数据结构,旨在简单、直观地处理关系型、标记型数据。Pandas 的目标是成为 Python 数据分析实践与实战的必备高级工具,其长远目标是成为最强大、最灵活、可以支持任何语言的开源数据分析工具。经过多年不懈的努力,Pandas 离这个目标已经越来越近了。

虽然 pandas 采用了大量的 NumPy 编码风格,但二者最大的不同是 pandas 是专门为处理表格和混杂数据设计的。而 NumPy 更适合处理统一的数值数组数据。

Pandas 数据结构

DataFrame 是 Pandas 最常用也是非常重要的一个对象,它是一个二维的数据结构,数据以行和列的表格方式排列。index+value+column
Series 是一个一维数据结构,包括 index 和 value。

Series


import pandas as pd
data = pd.Series([2, 9, 4], index=["xiao ming", "xiao hong", "xiao lan"])
data.index
data.values

DataFrame

属性:

  • info:基本信息
  • columns:列名
  • size
  • shape
  • len:查看某列的行数
  • count:查看某列的有效值(非空)的个数

66.jpg

方法

  • head():
  • tail():

创建 DataFrame

创建 DataFrame 的方式有很多种,一般比较常用的是利用一个字典或者数组来进行创建

import pandas as pd
import numpy as np
data = pd.DataFrame({'Height': [1.7, 1.8, 1.9],
'Weight': [140, 160, 180]},
index=["xiaoming", "xiaohong", "xiaowang"])
print(data)
arr = np.abs(np.random.randn(3, 2))
# print(arr)
data = pd.DataFrame(arr, columns=["Height", 'Weight'], index=[
"xiao ming", 'xiao hong', 'xiao zhang'])
print(data)
#设置总行名和总列名
data.index.name='students'
data.columns.name='body size'

也可以通过字典嵌套的方式把 index 揉进去(字典的key作为列名,作为字典的value写作{index:value}的形式 )
image.png

访问 DataFrame

简介:
1660837774129

使用字典方式访问 DataFrame。

  • 访问单列:DataFrame[‘column1_name’],DataFrame.column1_name(这种索引方式要保证列名合法)
  • 访问多列:DataFrame[[‘column1_name’,‘colunmn2_name’]]
  • 访问单列多行:DataFrame[‘column1_name’][m:n]
  • 访问多列多行:DataFrame[[‘column1_name’,‘colunmn2_name’]][m:n]

使用属性方式访问

  • 单列:DataFrame.column1_name
  • 单列多行:DataFrame.column1_name[m:n]

访问行的特殊方法

  • 访问 m 行到 n 行:DataFrame[:][m:n]
  • DataFrame.head/tail():访问前/后五行

整数标签的特殊情况

为了防止计算机不知道用户输入的索引是基于位置还是基于标签的,pd 整数标签的索引是基于标签的,也就是说我们不能像列表一样使用 DataFrame[-1]进行访问(仅针对整数作为索引的情况)

切片访问方法

DataFrame.loc[]访问
访问时主要采用[行索引或者条件,‘column1_name’]的方式对 DataFrame 进行切片,对行的指定要使用索引或者条件,对列的索引必须使用列名称,如果有多列,则还需要借助[]将列名称括起来。使用 loc 传入的行索引名称如果为一个区间,则前后均为闭区间

#条件表达式切片用法
print('条件表达式使用字典方式,xy123中x<5的x为:\n',
xy123.loc[xy123['x']<3,
['x']])#条件表达式使用字典方式
print('条件表达式使用属性方式,xy123中x>=8的x, y1为:\n',
xy123.loc[xy123.x>=8,
['x','y1']])#条件表达式使用属性方式
#df.loc[(df.Age>10) & (df.Age<50)]
#并列条件记得加括号,不然会报错!!!

需要注意的是 loc 函数的第一个参数不能直接传入整数,可以考虑送个列表进去
image.png
image.png

DataFrame.iloc[]访问
使用方法与 loc 相似,主要区别是该函数在使用时对列的索引可以用列索引号。使用 iloc 传入的行索引位置或列索引位置为区间时,则为前闭后开区间

#例3-46,iloc条件切片
#iloc内部传入表达式,进行条件切片,需使用.values属性
print('条件表达式使用字典方式,xy123中x<1的第1,3列数据为:\n',
xy123.iloc[(xy123['x']<1).values,[1,3]])#条件表达式使用字典方式

除了上述两种方法外,切片访问还可以使用 DataFrame.ix[] 方法进行切片,该方法可以看成是上述切片访问方法的结合。

image.png
image.png

查询函数

query()顾名思义,是pandas中专门执行数据查询的API,相较于前边提到的查询函数,query的查询方法更加灵活方便,query函数的基本结构为:
df.query('条件表达式')
其中条件表达式内可以直接用列名表示数据的字段名,还可以用index表示行索引,这样就可以完成df中基本的切片操作,除此以外,query函数内部还支持使用外部定义的变量(外部变量需要添加@),另外可以使用链式表达式,甚至可以直接使用python语句.几点注意:

  • 传入df的列名若不符合python的命名要求的话不能被正常解析,需要用``括起来
  • 支持python中的in或者not in操作符
  • 除了对常规字段进行条件筛选,query()还支持对数据框自身的index进行条件筛选,这种情况在用到的时候再查阅官方文档吧
data=pd.read_excel('./datasets/house_data.xlsx')
data.columns
data.query('综合评分 == 8.0').shape
target_type = ['简装修', '精装修']
data.query('装修 in @target_type&所属区=="某区"').shape


def test(s):
if s:
return True
else:
return False


data.query('装修 in @target_type&户型.apply(@test)').shape

像query这种直接用于查询的api,pandas中还有类似的可以用来添加列的函数eval(),这里不再过多说明,其实使用query函数的另外一个原因是这个函数的查询效率更高

更改名称

pd中的一个df一般会有两个位置有名称,一个是轴的名称(axis_name),一个是行或列的名称,两个名称可以在创建df时进行声明,也可以调用方法进行修改:

  • df.rename_axis(str,axis=0):修改轴的名称
  • df.rename(mapper,axis=0/1):用于修改行或者列标签的名称,mapper指的是一种映射关系,可以写一个字典,也可以引入一个函数(函数的输入参数为要修改的标签的名称),除了指明axis对行或者列标签的名字进行调整以外,还可以写成类似于index=mapper的形式,默认情况下,mapper匹配不到的值不会报错

更改 DataFrame 中的数据

更改值

  1. 更改值可以借助访问 DataFrame 的方法对值进行修改。
  2. 也可以通过建立一个 Series 通过赋值运算把两个中索引一致的位置进行修改
    image.png

添加或者删除行/列

  • 添加行或者列可以通过直接赋值的方法进行修改
xy123.loc[xy123['x']<=3,'x'] = 3#更改符合条件的记录的值
  • 删除行或者列需要借助 drop 函数(要调整 inplace 参数,感觉这个函数主要是用来不显示某些列的)。删除列也可以用 del 函数
DataFrame.drop(labels=None,axis=0,
index=None,columns=None,level=None,inplace=Flase,errors='raise')
#labels接收单个列名或者多个列名的列表或者列的索引或者行索引。
#inplace表示是否在原DataFrame上进行操作
#axis表示删除的行还是列,默认是0即删除行

Sorting and Ranking

  • df.sort_index(axis=1,ascending=False)
  • df.sort_values(by=[‘column_name1’,‘column_name2’])

排名使用 rank 方法,默认是通过取排名的平均值来处理排名相同的问题
df.rank(axis=,ascending=,method=)

import pandas as pd
import numpy as np
ser = pd.Series([7, 7, 9, 1, -8, 3])
print(ser)
print("*"*30)
print(ser.rank())
# 0 4.5
# 1 4.5
# 2 6.0
# 3 2.0
# 4 1.0
# 5 3.0

image.png

Transposing

借鉴 NumPy,可以通过 DataFrame.T 实现行列互换。

标签重命名

  • pd/se 创建时可以指定行列名字
  • df.index.names 属性可以用来修改标签名称
  • df.rename()方法进行修改(支持传入函数

索引

Index object

pd 的 Index 对象是用来作为保存轴标签的,而且是不可以被改写的!
一些方法:
image.png

#在创建df时规定列名与行名的一种方法
data = pd.DataFrame(np.arange(6).reshape((2, 3)),
index=pd.Index(['Ohio', 'Colorado'], name='state'),
columns=pd.Index(['one', 'two', 'three'],
name='number'))

除了创建时修改索引实例的名称,还可以通过以下方法进行修改:

df=pd.DataFrame(data,index,column)
df.index.names=
df.columns.names=

具体传入参数根据列标签和行标签进行适当调整即可(列表或者列表嵌套)。同样的对行的索引方式也支持对列使用。

多级索引

多级索引提供了一种以一个较低维度的形式访问高维数据的方法,每次一个维度的索引都相当于对原数据进行一次降维。多级索引建立与单个索引相似,只需将每一级各个值对应的索引名称传给 index 参数即可,每一级的索引单独组成一个列表,传入 index 的参数应为列表的嵌套。

import pandas as pd
import numpy as np
import pandas as pd
data = pd.Series([1, 1, 2, 3, 2, 5, 6],
[
['Grade One', 'Grade Two', 'Grade Two', 'Grade Two',
'Grade Three', 'Grade Three', 'Grade Three'],
['xiaoming', 'xiaoming', 'xiaohong', 'xiaowang',
'xiaoming', 'xiaohong', 'xiaowang']
],
name='Student_Info')
print(data.index)
print(data[:, 'xiaoming'])
data.index.names=['Grade', 'Student_Name']

MultiIndex 创建

可以利用 pd 的一些方法来创建一个多级索引对象,可以作为参数 index 的传入值:

  • pd.MultiIndex.from_arrays:创建方式类似于 zip 函数、
  • pd.MultiIndex.from_product:有点像 for 循环式的嵌套
pd.MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']],
names=['state', 'color'])
pd.MultiIndex.from_product([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']],
names=['state', 'color'])

Reordering and Sorting levels

多级标签下的每一级算一个 level,pd 提供了对 level 的重新排列和对任一 level 的排序

  • df/series.swaplevel():传递参数为两个 level 的数字或者是名称
  • df/series.sort_index(level=):按照任意 level 排序
data = pd.Series([1, 1, 2, 3, 2, 5, 6],
[
['Grade One', 'Grade Two', 'Grade Two', 'Grade Two',
'Grade Three', 'Grade Three', 'Grade Three'],
['xiaoming', 'xiaoming', 'xiaohong', 'xiaowang',
'xiaoming', 'xiaohong', 'xiaowang']
],
name='Student_ID')
print(data.index)
data.index.names=['Grade','Stu_Name']
print(data.swaplevel('Stu_Name','Grade'))
data.sort_index(level=1)

除了 sort_index,还要很多其他函数也支持按照某一个 level 进行函数操作,只需要在原来函数的基础上传入一个 level 参数即可(df.sum)。
多层索引的更多应用

索引重置

索引重置主要说的是索引调整(数目和顺序的调整)以及层次的调整(列取值变为行索引)。
pd 一个重要的方法是 reindex(),可以用来重新定义行/列索引的顺序以及内容(也可以用来增加新的index,该列或者行的值可以按照某种规则填充):

import pandas as pd
import numpy as np
data = pd.DataFrame({'Height': [1.7, 1.8, 1.9],
'Weight': [140, 160, 180]},
index=["xiaoming", "xiaohong", "xiaowang"])
print(data)
print(data.reindex(["xiaowang","xiaoming","xiaozhang"]))

对于数值类索引,在进行 reindex 时还可以进行缺失值的填充,一个方法是’ffill’(“forward-fills”),实现对缺失索引的前向填充:
image.png

image.png

一般来说,我们很少使用 df 的多级列标签,更多的情况是将列标签转化为行标签,这时就可以借助 df.set_index 方法:

  • drop:Bool,决定将列标签设置为行标签时原来的列标签是否保留
frame = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1),
'c': ['one', 'one', 'one', 'two', 'two',
'two', 'two'],
'd': [0, 1, 2, 0, 1, 2, 3]})
frame
frame2 = frame.set_index(['c', 'd'])
frame2

与该操作进行相反作用的函数是df.reset_index

重复标签下的轴索引

对重复标签的索引的返回值会是一个 Series,这会使得我们的代码变得复杂

索引重塑

多级标签的重塑主要借助 stack 和 unstack 方法:

  • stack:This “rotates” or pivots from the columns in the data to the rows(列值变为行索引)
  • unstack:This pivots from the rows into the columns(行索引变为列取值)
    两个函数默认都从最低level开始操作,然后将转换为另外一个轴的最低层级,可以传入 df 的层级名称或者数字来强制修改操作层级,另外就是堆叠数据(stack)的时候默认是删除缺失值的,可以通过调节 dropna 参数进行调整。
    series只有unstack()方法,df同时有stack()unstack()方法来转变为一个Series,两者的区别是原df所对应的index处于最低level还是最高level,转化成功的df列名变成index,列取值变为对应Series的值。

另外对于二级索引的 series,还可以借助 series.unstack()方法将二级索引拆成一个 dataframe,同样的也可以借助 stack 方法将一个 df 转化为一个 series(inverse operation)。

data = pd.DataFrame(np.arange(6).reshape((2, 3)),
index=pd.Index(['Ohio', 'Colorado'], name='state'),
columns=pd.Index(['one', 'two', 'three'],
name='number'))
result = data.stack()
df = pd.DataFrame({'left': result, 'right': result + 5},
columns=pd.Index(['left', 'right'], name='side'))
df
df.drop(index=('Ohio','three'),inplace=True)
#df.drop(index=['three'],level='number',inplace=True)#会删除二级索引为number的所有行
df
df.unstack('number')
df.unstack('state')
df.unstack('state').stack('side')
df.unstack('state').stack('side',dropna=False)
df.unstack('state').drop(columns='Ohio',axis=1,level=1)
df.unstack('state').drop(columns='Ohio',axis=1,level=1).stack('side')
df.unstack('state').drop(columns='Ohio',axis=1,level=1).stack('side',dropna=False)

查找特定值

  • df/ser.isin(list):返回布尔值
  • pd.index(list).get_indexer(to_match):根据 to_match 的情况返回一个对 list 的索引,值为 list 的索引值
  • image.png

分组

image.png
Pandas 提供了 DataFrame.groupby()方法,按照指定的分组键,将具有相同键值的记录划分为同一组,将具有不同键值的记录划分到不同组,并对各组进行统计计算。其格式如下:

DataFrame.groupby(by=None, axis=0, level=None, as_index=True,
sort=True, group_keys=True, squeeze=False, observed=False, **kwargs)

其中 by 接收分组键。DataFrame.groupby()返回一个称为GroupBy object的对象。实际上分组后的数据对象 GroupBy 类似 Series 与 DataFrame,是 pandas 提供的一种对象。
python 中可以作为分组键的类型:

  • 列名
  • 和分组数据等长的数组或者列表
  • 一个指明分组名称分组值关系的字典或者 series
  • A function to be invoked on the axis index or the individual labels in the index。
  1. 利用函数进行分类需要注意的是传入参数是df的行索引,目前我觉得使用这个自定义函数分类的方法主要是使用loc(x,)方法获得所需的列来进行运算
  2. 分组的操作轴默认为 axis=0,也可以进行调整
  3. 对于多级标签的对象,也可以指定 level 参数
  4. 调整 as_index 参数返回不带行标签的索引结果(取消两个及以上分组键的分组结果的多级索引)
  5. 调整 group_keys 参数,决定是否显示分组键索引
  6. 一般用分组键的取值作为行索引,如果是传入一个函数用来分组,那么默认借助函数的返回值作为索引。
df = pd.DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'],
'key2' : ['one', 'two', 'one', 'two', 'one'],
'data1' : np.random.randn(5),
'data2' : np.random.randn(5)})
#列名
df.groupby('key1').first()
#等长的series作为分组键
df['data1'].groupby(df['key1']).first()
#列表或者数组
df['data1'].groupby([df['key1'],df['key2']]).first()
states = np.array(['Ohio', 'California', 'California', 'Ohio', 'Ohio'])
years = np.array([2005, 2005, 2006, 2005, 2006])
df['data1'].groupby([states, years]).first()
#Grouping with Dicts and Series
people = pd.DataFrame(np.random.randn(5, 5),
columns=['a', 'b', 'c', 'd', 'e'],
index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis'])
people.iloc[2:3, [1, 2]] = np.nan # Add a few NA values
people
mapping = {'a': 'red', 'b': 'red', 'c': 'blue',
'd': 'blue', 'e': 'red', 'f' : 'orange'}
by_column = people.groupby(mapping, axis=1)
by_column.sum()
map_series = pd.Series(mapping)
map_series
people.groupby(map_series, axis=1).count()
#Grouping with Functions
people.groupby(len).sum()
columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', 'JP', 'JP'],
[1, 3, 5, 1, 3]],
names=['cty', 'tenor'])
hier_df = pd.DataFrame(np.random.randn(4, 5), columns=columns)
hier_df
hier_df.groupby(level='cty', axis=1).count()

需要注意的是,分组键的值为空的数据将会被移出。

Groupby object

分组后生成的对象支持迭代,默认一个迭代对象是两个元组,分别包含组名和数据。元组的具体情况要根据分组的情况而定(分组键的数量之类的)。

for (k1, k2), group in df.groupby(['key1', 'key2']):
print((k1, k2))
print(group)
#将分组结果转化为字典形式的方法
pieces = dict(list(df.groupby('key1')))
pieces['b']

实例的属性:

  • groupby.groups:返回每组中数据的索引,字典类型。可以使用get_group('group name')方法返回组名为 group name 的全部记录。
    分组后的对象其实可以视作一个新的 df 或者 se(SeriesGroupBy object),名字即为分组键的值(如果是通过传递函数进行分组那么索引值就是函数的返回值),当数据集比较大时,我们有时候只希望对分组结果的部分列进行运算,因此可以写成类似于下边的形式:
    df.groupby(['key1', 'key2'])[['data2']].mean()

分组后可以进行的操作:

  • 描述性统计分析(见描述性统计分析)
  • 聚合运算

使用 GroupBy 进行描述性统计

  • 对分组结果 GroupBy object 的描述性统计
    • GroupBy object.count()——返回每组记录数量,包括缺失值。
    • GroupBy object.max()——返回组内最大值。
    • GroupBy object.min()——返回组内最小值。
    • GroupBy object.sum()——返回每组的和。
    • GroupBy object.mean()——返回每组的均值。
    • GroupBy object.std()——返回每组的标准差。
    • GroupBy object.median()——返回每组的中位数。
    • GroupBy object.size()——返回每组的大小。
#例4-10 对汽车销售数据表进行分组聚合,观察各个描述性统计
vs['date']=pd.to_datetime(vs['date'])#将'date'转换成日期型
#按照日期进行分组
vsGroup = vs.groupby(by='date')
#各个特征使用相同的函数统计计算
print('汽车销售数据表按日期分组后前5组每组的数量为:\n',
vsGroup.count().head())

基本用法

Pandas 读写文件

Pd 提供了许多读写结构化数据的为 df 的函数:
image.png
image.png
由于pd特殊的数据结构,在读写或者保存数据时需要注意的是一定要声明索引,不然它会使用默认的索引,这也意味着当我们将有默认索引的df进行保存时,也会将默认索引保存进数据文件中,这点一定要注意

算术运算和数据对齐

pd 最重要的一个功能是可以对不同索引的对象进行算术运算。以加法为例,它会匹配索引相同(行和列)的进行算术运算,再将索引不匹配的数据视作缺失值,但是也会添加到最后的运算结果中,从而组成加法运算的结果。

  • 如果想给缺失值赋予自己想要的值,则需要利用方法,以 add 为例
    df1.add(df2,fill_value=0)

image.png
r 表示翻转参数

Df 和 Ser 之间的算术运算

与数组的不同维度的数组进行算术运算的方法相似,pd 会将 df 拆成 n 个一维的分别与 ser 进行匹配然后进行算术运算

By default, arithmetic between DataFrame and Series matches the index of the Series
on the DataFrame’s columns, broadcasting down the rows:

如果有不匹配的索引,那么将会重新进行索引来形成一个联合:
image.png
image.png
想要改变逐行进行匹配的广播机制,需要借助df.sub(ser,axis='index')方法:
image.png

数学运算

Numpy 基于元素的公式运算对于 pd 也适用

  • np.abs(df)
  • df.apply(f,axis=’’)

数据转换

数据转换

数据转换主要是通过定义一些函数来实现映射,下边介绍一种使用 map 函数的数据转换方法:

The map method on a Series accepts a function or dict-like object containing a map‐
ping

data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon',
'Pastrami', 'corned beef', 'Bacon',
'pastrami', 'honey ham', 'nova lox'],
'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})
print(data)
meat_to_animal = {
'bacon': 'pig',
'pulled pork': 'pig',
'pastrami': 'cow',
'corned beef': 'cow',
'honey ham': 'pig',
'nova lox': 'salmon'
}
lowercased = data['food'].str.lower()
lowercased
data['animal'] = lowercased.map(meat_to_animal)
data
data['food'].map(lambda x: meat_to_animal[x.lower()])

同样的方法,也可以用来对 df 的轴标签进行重新索引,只不过操作对象变成了 df.index

df.replace()

df.replace()主要接受两个参数,第一个参数表示被替换值,第二个参数表示替换值,这两个参数可以是两个等长的列表(一一匹配),亦可以是一个字典键值对匹配即可。
在多数情况下,对时间类型数据进行分析的前提就是将原本为字符串的时间转换为标准时间类型。pandas 继承了 NumPy 库和 datetime 库的时间相关模块,提供了 6 种时间相关的类。

df.str.replace

The data.replace method is distinct from data.str.replace, which performs string substitution element-wise. (按元素执行字符串匹配)We look at these string methods on Series later in the chapter.

时间序列数据

A basic kind of time series object in pandas is a Series indexed by timestamps, which is often represented external to pandas as Python strings or datetime objects

创建

pd 的to_datetime能够将字符串解析为时间对象,并会将缺失值记作‘NAT’,该函数解析之后会返回一个 timestamp 对象,该对象的

NaT (Not a Time) is pandas’s null value for timestamp data.

Timestamp–时间点

多个 timestamp 对象储存在一个 series 或者 df 或者列表中时,这些对象是通过 datetimeindex 组织起来的。

常用属性
  • 在多数涉及时间相关的数据处理,统计分析的过程中,需要提取时间中的年份,月份等数据。使用对应的 Timestamp 类属性就能够实现这一目的。
  • 结合 Python 列表推导式,可以实现对 DataFrame 某一列时间信息数据的提取

image.png

year1 = [i.year for i in order['lock_time']]
print('lock_time中的年数据前5个为:',year1[:5])
month1 = [i.month for i in order['lock_time']]
print('lock_time中的月数据前5个为:',month1[:5])
day1 = [i.day for i in order['lock_time']]
print('lock_time中的日数据前5个为:',day1[:5])
dayname1 = [i.day_name for i in order['lock_time']]
print('lock_time中的日期数据前5个为:',dayname1[:5])

DatetimeIndex 与 PeriodIndex 函数

除了将数据字原始 DataFrame 中直接转换为 Timestamp 格式外,还可以将数据单独提取出来将其转换为 DatetimeIndex 或者 PeriodIndex。DatetimeIndex 是用来指代一系列时间点的一种数据结构,而 PeriodIndex 则是用来指代一系列时间段的数据结构。
转换为 PeriodIndex 的时候需要注意,需要通过freq 参数指定时间间隔,常用的时间间隔有 Y 为年,M 为月,D 为日,H 为小时,T 为分钟,S 为秒。两个函数可以用来转换数据还可以用来创建时间序列数据,其参数非常类似。

image.png
image.png

Timedelta–不同单位的时间

Timedelta 是时间相关的类中的一个异类,不仅能够使用正数,还能够使用负数表示单位时间,例如 1 秒,2 分钟,3 小时等。使用 Timedelta 类,配合常规的时间相关类能够轻松实现时间的算术运算。目前 Timedelta 函数中时间周期中没有年和月。所有周期名称,对应单位及其说明如下表所示。
image.png
image.png

TimedeltaIndex

一组 Timedelta 构成的 Index,可以用来作为 Series 或者 DataFrame 的索引

访问

时间序列数据的访问其实可以参考 pandas 的 series 的访问方式,既可以使用 se.index[2]获取行索引的值进行访问,也可以直接调用行索引值进行访问,不过比较方便的是,索引值可以是一个可以被翻译为日期的字符串(功能比较灵活,甚至可以输入年份的字符串匹配所有符合年份的数据,也允许使用:作为索引标签)

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = 'all'
from datetime import datetime
dates = [datetime(2011, 1, 2), datetime(2011, 1, 5),
datetime(2011, 1, 7), datetime(2011, 1, 8),
datetime(2011, 1, 10), datetime(2011, 1, 12)]
ts = pd.Series(np.random.randn(6), index=dates)
ts
stamp = ts.index[2]
ts[stamp]
ts['1/10/2011']
ts['20110110']
'''
Because most time series data is ordered chronologically, you can slice with time‐
stamps not contained in a time series to perform a range query:
'''
ts[datetime(2011, 1, 7):]

需要注意的是切片访问相当于在源时间序列上创建一个新的 view(和 numpy 一样的),言外之意是并没有 copy 数据,任何对 view 的修改会作用于原来的数据。
截断访问:timeseries.truncate:
ts.truncate(after='1/9/2011'

连续序列产生&Frequencies

pd.date_range(start, periods=100, freq,normalize=False),‘periods’参数用来指定生成的长度,一般在 start 或者 end 缺失时会用到(该函数默认按照天为间隔生成 DatetimeIndex 对象)。
freq的可选参数有:

freq 参数传入的参数除了上述这种形式,还可以在基础的时间频率的基础上加一些数字,例如’4H’(Putting an integer before the base frequency creates a multiple:).还可以传入一下类似于’1h30min’的字符串,这个也可以被很好的解析。

pd.date_range('2000-01-01', '2000-01-03 23:59', freq='4h')
#week of chance enables you to get dates like the third Friday of each month
rng = pd.date_range('2012-01-01', '2012-09-01', freq='WOM-3FRI')
list(rng)

指定频率的时间序列生成(频率转换和重采样)

pandas 支持处理在格式上间隔不相等的时间序列数据,但是有的时候我们希望生成或者转化成一些间隔相同时间序列数据。

Fortunately pandas has a full suite of standard time series frequencies and tools for resampling, inferring frequencies, and generating fixed-frequency date ranges. For example, you can convert the sample time series to be fixed daily frequency by calling resample.

Frequencies and Date Offsets

对于基本的时间间隔,pandas 都提供了一个基础的 frequency,然后这些基础的 frequency 还可以借助乘法器组成 pd 里常用的一些 frequency。

For each base frequency, there is an object defined generally referred to as a date offset

不均匀的间隔被叫做 anchored offsets。

from pandas.tseries.offsets import Hour, Minute
hour = Hour()
hour
#define a multiple of an offset by passing an integer
four_hours = Hour(4)
four_hours

pd 的这些 date offsets 也可以用在 datetime 和 timestamp 对象身上。

from pandas.tseries.offsets import Day, MonthEnd
"""
If you add an anchored offset like MonthEnd, the first increment will “roll forward” a
date to the next date according to the frequency rule:
"""
now + MonthEnd()
now + MonthEnd(2)

感觉上边运算方式多多少少有些奇怪,这时可以考虑 anchored offset 的两个方法rollforwardrollback来 roll dates bakward and forward。

offset = MonthEnd()
offset.rollforward(now)
offset.rollback(now)
#创新性地用在groupby中
ts = pd.Series(np.random.randn(20),
index=pd.date_range('1/15/2000', periods=20, freq='4d'))
ts
ts.groupby(offset.rollforward).mean()
ts.resample('M').mean()

Period–时间跨度或时间段

Periods represent timespans, like days, months, quarters, or years. The Period class represents this data type, requiring a string or integer and a frequency from Folloing Table.

You have two ways to create a Period Object:

  • 利用pd.period(,freq)# 第一个参数可以是整数也可以是字符串,也可以是一个 frequency
  • 利用pd.period_range()函数生成一个 Period 序列

Period 对象支持数学运算(可以直接加减整数,感觉可以看做一个相同 freq 的对象),如果两个对象的 frequency 相同的话,他们的差则会是整数(the number of units between them)

PeriodtimeIndex

借助pd.period_range()或者pd.PeriodIndex(str/list,freq=)来产生,一组 Period 构成的 Index,可以用来作为 Series 或者 DataFrame 的索引。
image.png

Period Frequency Conversion

Periods and PeriodIndex objects can be converted to another frequency with their asfreq method.

p = pd.Period('2007', freq='A-DEC')
p
p.asfreq('M', how='start')
p.asfreq('M', how='end')
p = pd.Period('2007', freq='A-JUN')
p
p.asfreq('M', 'start')
p.asfreq('M', 'end')
p = pd.Period('Aug-2007', 'M')
p.asfreq('A-JUN')

对于不同 frequency 的 period,大的 period 可以看成一系列小的 period 的排列,而两个不同 frequency 的 Period 转换也可以视为是 superperiod 与 subperiod 的转换。

Shifting (Leading and Lagging) Data

在对 df 或者 series 数据(包括时间序列)进行转换时有的时候可能需要把根据时间整体前移或者后移,这个时候就可以借助 df 和 se 的方法 shift,这种移动只是数据值的移动,索引不会改变(对于时间类型索引的数据,也可以通过指定 freq 参数来对索引进行整体的调整)。这种方法比较常见的应用场景是计算环比。

ts = pd.Series(np.random.randn(4),
index=pd.date_range('1/1/2000', periods=4, freq='M'))
ts
ts.shift(2)
#计算环比
ts / ts.shift(1) - 1

总体的随机排列(permutation)和随机抽样

随机排列

随机排列可以借助 np.random.permutation(n)实现对 n 维数组的行索引进行一个随机排序,返回值为一个一维数组。然后可以利用 df.iloc 或者 df.take 函数来得到随机排序后的 df。

随机抽样

随机抽样用到的是 df.sample(n)函数,该函数返回值为对于 df 以行为抽样单位进行的随机抽样,返回值是从总体随机抽出的 n 行组成的 df(默认不可以重复,可以调整参数)

import pandas as pd
import numpy as np
a = np.random.randn(20).reshape((5, 4))
a = pd.DataFrame(a)
sampler = np.random.permutation(5)
print(sampler)
print(a)
print(a.iloc[sampler])
print(a.take(sampler))
print(a.sample(3))
print(a.sample(3, replace=True))

数据整理

数据堆叠

数据堆叠的目的是通过建立多层级索引的方式将数据的列索引或者行索引转为行索引/列索引,这样使得数据集变得更长或者更宽。

多级标签的重塑主要借助 stack 和 unstack 方法:

  • stack:This “rotates” or pivots from the columns in the data to the rows
  • unstack:This pivots from the rows into the columns
    两个函数默认都从最低层级开始操作,然后将转换为另外一个轴的最低层级,可以传入 df 的层级名称或者数字来强制修改操作层级,另外就是堆叠数据(stack 方法)的时候默认是删除缺失值的,可以通过调节 dropna 参数进行调整。
    另外对于二级索引的 series,还可以借助 series.unstack()方法将二级索引拆成一个 dataframe,同样的也可以借助 stack 方法将一个 df 转化为一个 series(inverse operation)。pd 的多级索引设置成了单独的对象(MultiIndex)
data = pd.DataFrame(np.arange(6).reshape((2, 3)),
index=pd.Index(['Ohio', 'Colorado'], name='state'),
columns=pd.Index(['one', 'two', 'three'],
name='number'))
result = data.stack()
df = pd.DataFrame({'left': result, 'right': result + 5},
columns=pd.Index(['left', 'right'], name='side'))
df
df.drop(index=('Ohio','three'),inplace=True)
df.drop(index=['three'],level='number',inplace=True)#会删除二级索引为number的所有行
df
df.unstack('number')
df.unstack('state')
df.unstack('state').stack('side')
df.unstack('state').stack('side',dropna=False)
df.unstack('state').drop(columns='Ohio',axis=1,level=1)
df.unstack('state').drop(columns='Ohio',axis=1,level=1).stack('side')
df.unstack('state').drop(columns='Ohio',axis=1,level=1).stack('side',dropna=False)

df拼接

This section focuses on tools to help combine, join, and rearrange data.
这一部分主要介绍了一些将多个 df 的数据组合起来的一些方法:

  • Join and Merge 部分主要侧重于类似于 SQL 查询的多表查询和联合的方法
  • pd.concat() 和 numpy 的 concatenate 有些类似,主要应用于沿某一个轴进行拼接,也可以使用定义在DataFrame和Series对象的类方法append()实现相同的功能。
  • combine 方法主要用来对两个表的数据进行 combine,具体 combine 的方法依据传递的函数的返回值
  1. 多个 dataframe 连接(通过 index 匹配进行)(Join and Merge)
  • 通过一个或多个键将两个数据集的列连接起来(完成 SQl 的 join 操作):pandas.merge()函数和pandas.DataFrame.join()方法,多表的连接要把被连接的 df 名称以列表的形式传入
    • pd.merge(df1,df2,on=‘column_name’)
    • pd.merge(df1,df2,left_on=’’,right_on=’’)
    • how:表示数据库的 join 方式,默认是 inner join。可选的有’left’,‘right’,‘output’
    • 在对多个表进行 join 的时候,行索引会被丢弃
      image.png
    • 观察参数表可知也可以通过一个的行索引与另外一个表的列索引进行 join(甚至适用于行标签为多级索引的情况)
    • df.join()方法适用于那些 index 相似或者相同且没有重复列的 dfs,默认使用行索引匹配也支持一个 df 的行索引英语另一个 df 的列索引 join 起来
left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],
'value': range(6)})
right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])
print(left1)
print(right1)
left1.join(right1, on='key')
left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]],
index=['a', 'c', 'e'],
columns=['Ohio', 'Nevada'])
right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]],
index=['b', 'c', 'd', 'e'],
columns=['Missouri', 'Alabama'])
print(left2)
print(right2)
right2
left2.join(right2, how='outer')、
another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]],
index=['a', 'c', 'e', 'f'],
columns=['New York', 'Oregon'])
another
left2.join([right2, another])
left2.join([right2, another], how='outer')
  1. dfs 沿轴拼接(Concatenating)

沿轴拼接的好处:

  • 拼接后的数据可以看到数据的来源
  • 拼接的时候需要删除默认的整数标签
  • join 或者 merge 方法实现的其实是表的横向拼接,需要纵向拼接时的情况

df 的拼接是从 numpy 的拼接引入的,选择沿着不同的轴进行匹配会产生不同的结果,具体匹配情况可以类比数组的拼接,区别是沿着 axis=1 进行叠加时会考虑行索引相同的进行合并。

  • 数据横向、纵向堆叠:pandas.concat([],axis=,join=)(可以通过 keys 来在合并轴上创建层次索引)
s1=pd.DataFrame(
{
'height':[1.4,1.5,1.6],
'weight':[160,180,200]
},
index=['xiaoming','xiaohonng','xiaowang']
)
s1
s2=pd.DataFrame(
{
'age':[14,5,16],
'sex':['male','female','male']
},
index=['xiaoming','xiaohonng','xiaowang']
)
s2
pd.concat([s1,s2])
pd.concat([s1,s2],axis=1,keys=['体型','个人信息'])
#写成字典形式
pd.concat({'体型': df1, '个人信息': df2}, axis=1)
pd.concat([s1,s4],keys=['表1','表2'])

image.png
image.png

combine 也是适用于 index 部分或者全部相似的情况,combine 的其实是两个表的值,有点类似于 numpy 的 where 函数,是 if-else 功能的一个等价表示。两种使用方法,一种是 np.where()方法,一种是 pd.combine(self,df,func)(func 为一个传入两个参数的函数。

import numpy as np
a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan],
index=['f', 'e', 'd', 'c', 'b', 'a'])
b = pd.Series(np.arange(len(a), dtype=np.float64),
index=['f', 'e', 'd', 'c', 'b', 'a'])
b[-1] = np.nan
a
b
np.where(pd.isnull(a), b, a)
b.combine(a,max)
  • pd.combine_first(self,other):将 self 的空值用但缺失值会用 other 的对应值进行填充。

df重构

数据的重塑主要指的是将数据的shape进行变化,本质上其实是使用stack()unstack()方法,只是因为比较常用而进行了一个封装(一般来说我们用于处理的数据是不存在索引的,或者说往往会用连续数字做一个简单的索引)

行列值的重塑(数据透视long→wide)

这部分主要介绍的是 pivot 函数,pivot 函数实现的是数据从长的形式向宽的形式的转换,一般意义上来说,我们认为存储在 csv 或者数据库中的文件属于长的格式(有多个索引的 key 参数,只有一个 value)。pivot 函数要做的其实就是根据一个 key 的离散取值来把长的表给变成宽的表。
df.pivot('column_1','column_2','column_3)
df 是一个我们希望转化的长的表,上述语句的意思其实就是我们希望用原来 df 的’column_1’作为行索引,'column_2’作为列索引,'column_3’作为值对 df 进行一次重整:
image.png
如果不指定最后一个参数,默认会创建多级索引(等价于:df.set_index(['column_1','column_2]).uhstack('column_2')(pivot()其实就是用 set_index()创建层次化索引,再用 unstack()重塑)

逆透视wide→long

pivot 的一个逆运算是 pd.melt(),这个是用来将多列转化一列:
pd.melt(df, id_vars=['key'], value_vars=['A', 'B'])
该函数最后返回的是一个以id_vars为保留列,以value_vars中的列名作为列名称为’variable’的列的取值的,'value’列为原列对应取值的一个df。
与之类似的还有pd.lreshape()方法,该函数主要用来将有相同含义的列进行一个整合,形如以下的数据集:
1659768405465
使用melt()方法的整合结果为:
1659768452140
使用lreshape()方法的整合结果为:
1659768438346

数据预处理

pd 在对数据进行处理时会默认不考虑缺失值(数值型数据的缺失值会被写作 NaN,另外需要注意的是 Python 内置的 None 也会被视为缺失值)

Data Cleaning

去重

  • 返回不重复数据:df.unique()
  • 统计值:df.value_counts()(默认按列计算好像,返回的还是一个 dataframe,值有更改)
  • 查找是否存在重复数据:df.duplicated()(返回布尔值,默认将已经观察到先前有之后的行返回 True 这个需要调整 keep 函数,默认查找全部列,也可以进行调整)data.drop_duplicates(['k1']) (只查看 k1 列)
  • 去除重复数据:pandas.DataFrame(Series).drop_duplicates()方法。

缺失值处理

缺失值识别

  • pandas.DataFrame.isnull()和 pandas.DataFrame.notnull()方法识别缺失值和非缺失值,两个方法会返回一个与输入同型的布尔df。
    • df.isnull().any():判断存在缺失值的列(any()Return whether any element is True, potentially over an axis)
    • df.isnull().sum():统计每列缺失值的个数
#将数据按照指定列分组后统计每组中每列的缺失值情况,筛选出指定列存在缺失值的组并升序排列
data_c=data.groupby('所在小区').apply(lambda x: x.isna().sum())
data_c[data_c['建筑类型'] > 0]['建筑类型'].sort_values(ascending=False)

缺失值删除

  • 对缺失值,可以使用 pandas.DataFrame.dropna()方法删除记录或特征(默认删除含有缺失值的行,可以修改 how 参数进行调节,也可以调节 thresh 参数控制删除指定数量缺失值的行,亦可通过调节subset=[col_name]参数来指定删除指定列存在缺失值的行)

缺失值补充

  • df.isnull().T.any() == True返回缺失值所在行的索引
  • 也可以使用 pandas.DataFrame.fillna()方法进行常量填补()
    • 输入字典来指定每一列的填补值
    • 调整 inplace 参数直接在原 df 上修改
    • method 参数可以选择填补方法,使用的方法有’ffill’,‘bfill’
    • limit 参数可以指明对缺失值多少个的值进行填补
    • 也可以利用原数据集的均值进行填补(data.fillna(data.mean())
  • 或者使用 pandas.DataFrame.interpolate(), SciPy 的 interpolate 方法进行线性差值、多项式插值、样条插值。
#拉格朗日插值方法
from scipy.interpolate import lagrange
#自定义列向量插值函数,s为列向量,n为被插值的位置,k为取前后的数据个数, 默认5
def ployinterp_columns(s, n, k=5):
y = s[list(range(n-k,n)) + list(range(n+1,n+1+k))] #取数
y = y[y.notnull()] #剔除空值
return lagrange(y.index, list(y))(n) #插值并返回插值结果
for i in data.columns: #逐个元素判断是否需要插值
for j in range(len(data)):
if (data[i].isnull())[j]: #如果为空即插值
data[i][j] = ployinterp_columns(data[i],j)

截断

df.clip()

异常值检测

异常值的检测主要是根据已有的需求对 df 进行一个筛选,然后通过赋值操作来剔除不需要的异常值。

import pandas as pd
import numpy as np
a = np.random.randn(16).reshape(4, 4)
print(a)
print("="*80)
a[a > np.mean(a)] = None
print(a)

转换数据–哑变量处理(Index/dummy Variables)

  • 当特征为分类型时,例如职业、学历、血型、疾病严重程度等等,通常会将原始的多分类变量转化为数值型,这种转化后的特征(或变量)称为哑变量,又称为虚拟变量、虚设变量或名义变量。
  • 它是人为虚设的变量,通常取值为 0 或 1,来反映某个变量的不同属性
  • 哑变量的处理过程实际上就是分类型特征的值的编码过程。Pandas 提供了哑变量处理方法pandas.getdummies().

这个地方联想一下独热编码,哑变量处理其实进行一个 one-hot 编码

from dataclasses import replace
import pandas as pd
df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
'data1': range(6)})
print("初始的df为:\n", df)
print("根据key值列得到的指示变量:\n", pd.get_dummies(df['key']))
#可以调整prefix参数给指示变量加上前缀名称

image.png

字符串数据

对字符串的操作有使用字符串内置函数和 re 库进行正则表达式匹配两种方法,pd 将这两种方法都加在了 df 或者 series 对象的 str 属性中,通过 df/series.str.method_name 就可以使用了。直接调用内置的字符串处理函数会有一个问题是这些函数并没有定义 nan 数据的处理方式,因此最好借助 str 属性进行调用。感觉 series.str 就可以看成是一个字符串对象,然后就可以对这个对象调用一些字符串用的方法,包括索引什么的(通过装饰器把函数当属性用)。

data = {'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com',
'Rob': 'rob@gmail.com', 'Wes': np.nan}
data = pd.Series(data)
data
data.isnull()
data.str.contains('gmail')
matches = data.str.match(pattern, flags=re.IGNORECASE)
#对查询结果的索引方式,直接索引或者借助str.get
matches.str.get(1)
matches.str[0]

就是因为有 nan 值的存在导致一些本来可以直接用的内置函数要做一些调整:
image.png
image.png0

重置索引

数据清洗时,会将带空值的行删除,此时 DataFrame 或 Series 类型的数据不再是连续的索引,可以使用reset_index()重置索引。

数据筛选

image.png

数据标准化

数据分组和聚合操作

After loading, merging, and preparing a dataset, you may need to compute group statistics
or possibly pivot tables for reporting or visualization purposes. pandas provides a
flexible groupby interface, enabling you to slice, dice, and summarize datasets in a
natural way.

分组的介绍参见前面内容,这里主要介绍聚合。

聚合

聚合数据的三种方法.png
除了 Series 方法 quantile 函数不支持对 groupby 后的 df 直接使用以外,常见的统计描述函数都可以直接在 dfGroupBy 上进行聚合操作,为了使用我们自定义的聚合函数,这里引入 python 的一些函数

使用 agg 方法聚合数据

agg,aggregate 方法都支持对每个分组应用某函数,包括 Python 内置函数或自定义函数。同时这两个方法能够也能够直接对 DataFrame 进行函数应用操作。
在正常使用过程中,agg 函数和 aggregate 函数对 DataFrame 对象操作时功能几乎完全相同,因此只需要掌握其中一个函数即可。它们的参数说明如下表。

DataFrame.agg(func, axis=0, *args, **kwargs)
DataFrame.aggregate(func, axis=0, *args, **kwargs)

func 接收函数、字符串、字典,或函数与字符串的列表。

  • 传入一个函数名组成的列表,则会将每一个函数的函数名作为返回值的列名,如果不希望使用函数名作为列名,可以将列表中的元素写成类似’(column_name,function)'的元组形式来指定列名为name。如果想指定聚合列的列名,可以写成new_column_name=(column_name,function)的形式,多列就并列传入多个参数即可。
  • 传入一个字典格式

image.png

自定义函数时的一点注意事项
自定义的函数应该是一个用来聚合数组类型数据的函数。这里和 quantile 函数不能用是一样的原因。pd 的统计描述函数是从 np 继承过来的因此写成 np.min 没有差别

使用 apply 方法聚合数据

image.png

apply splits the object being manipulated into pieces, invokes the passed function on each piece, and then attempts to concatenate(pd.concat) the pieces together.

如果只是对 DataFrame 对象或分组对象进行统一的统计计算,也可以使用 groupby 对象的方法 apply,其格式为:

DataFrame.apply(func, axis=0, broadcast=None, raw=False,
reduce=None, result_type=None, args=(), **kwds)

其中可变长参数主要是输入一些 func 需要的其他参数
image.png

#巧妙利用apply函数拆解字典结构
df=pd.DataFrame({'info':[{'姓名': '琪亚娜·卡斯兰娜', '生日': '12月7日', '外号': '草履虫'}
, {'姓名': '布洛妮娅·扎伊切克', '生日': '8月18日', '外号': '板鸭'}
, {'姓名': '德丽莎·阿波卡利斯', '生日': '3月28日', '外号': '德丽傻', '武器': '犹大的誓约'}
]},index=['001','002','003'])
df
df['info'].apply(pd.Series)

使用pd的apply函数,我们可以使用自定义参数来实现很多功能,自定义函数过程中一个非常重要的点在于使用apply函数时,在对自定义函数func进行apply时,传入func的参数是对DataFrame的一行或者一列数据(默认是一列数据,通过axis参数进行调整),这时传入func的相当于一个Series(index为行索引或者列名),这一点需要格外注意。如果apply本身传入的是一个Series,那么传入func的就是一个值。

使用aggapply聚合数据的一个区别体现函数的作用对象上,在自定义函数时,我们使用agg时默认聚合函数的输入是一个数组,而apply的聚合函数的输入参数是一个DataFrame,我想这也一定程度上解释了为什么apply函数会更常用一些。

使用 transform 方法聚合数据

Pandas 提供了transform()方法对 DataFrame 对象和分组对象的指定列进行统计计算,统计计算可以使用用户自定义函数。
其格式为:

pythonDataFrame.transform(func,*args,**kwargs)

其中func接收用于统计计算的函数。其形式为一般为lambda x: f(x). 其中 x 为 DataFrame 或分组对象 GroupBy object 的列的泛指。

#Z-score标准化,即缩放为均值为0,标准差为1
print('汽车销售表分组后实现组内Z-score标准化后前五行为:\n',
vsGroup.transform(lambda x: (x - x.mean()) /
x.std()).head())

透视表和交叉表

数据透视表创建.png

使用 pivot_table 创建透视表

#fill_value表示空值的填充值
pythonpandas.pivot_table(data, values=None, index=None,
columns=None, aggfunc='mean', fill_value=None, margins=False,
dropna=True, margins_name='All')
DataFrame.pivot_table(values=None, index=None,
columns=None, aggfunc='mean', fill_value=None, margins=False,
dropna=True, margins_name='All')

参数名称及含义:

  • data:创建表的数据
  • index:行分组键,可以写成列表表示多级索引。
  • columns:列分组键
  • values:数值计算键
  • aggfunc: 聚合函数 ,默认为平均值函数
  • margins: 接收布尔值,表示是否对透视表的行和列进行汇总
  • dropna:是否删除全为Nan的列,默认为False

实际应用过程中出现的一个问题是在做数据透视表时行分组建和计算键不能是同一个键,例如对于一个df的a列,该列存储的是不同类型的文本数据,我想要统计每一个文本数据出现的次数,这个时候就既需要a列作为索引键,又同时需要聚合该列的数据,这种情况下该函数会报错。这个时候一个替代方法是:df.groupby("district")['companySize'].value_counts(),就会返回一个以district和companysize为行索引统计company频次的一个表。

透视表其实一定程度上来说就是对分组方法(groupby())的一个封装。

使用 crosstab 创建交叉表

A cross-tabulation (or crosstab for short) is a special case of a pivot table that computes group frequencies.

交叉表是一种特殊的数据透视表,它仅指定一个特征作为行分组键,一个特征作为列分组键,是为交叉的意思。

pandas.crosstab(index, columns, values=None, rownames=None,
colnames=None, aggfunc=None, margins=False, margins_name='All'
, dropna=True, normalize=False)
  • index:生成交叉表的行索引标签
  • columns:生成交叉表的列标签
  • value:表格的值,既可以是数组或者 series 也可以是数组列表

其它参数与 pandas.pivot_table()方法类似。注意rownames为行分组键的名称,默认应该是 index 参数对应的列名。

#以vehicle_type作为行、salesman_name为列
vsCross = pd.crosstab(index=vs['vehicle_type'],
columns=vs['salesman_name'],
values = vs['counts'],aggfunc = np.sum)
print('以vehicle_type和salesman_name为分组键、
counts为值的汽车销售数据交叉透视表前10行10列
为:\n',vsCross.iloc[:10,:10])

转换数据–DataFrame 数据离散化

在进行数据分析时,需要先了解数据的分布特征,如某个值的出现频次、不同的取值区间样本的多少,需要对数据的分布特征进行初步的了解。对于非数值类数据的统计可以使用astype方法将目标特征的数据类型转换为category类别

  • Pandas 提供了按照变量值域进行等宽分割的pandas.cut()方法。
  • 用户也可以使用 pandas.DataFrame.quantile()方法获得特征的具有相同位置间隔的不同分位数,使用pandas.cut()方法按照各个分位数切割区间,设计等频法离散化连续数据。

统计等值样本出现的频数

要统计相同值样本出现的频数,Pandas 提供了pandas.series.value_counts()方法。其格式如下:

pythonSeries.value_counts(normalize=False, sort=True,
ascending=False, bins=None, dropna=True)

pandas.series.value_counts()方法将 series 中的相同值看作一个类别分别返回各个类别的记录数量,即频次,并根据 sort 的值决定是否按频次排序。acending按照频数升序或者降序。

统计落入每个区间的频数(等宽法离散数据)

使用pandas.cut()方法和pandas.series.value_counts()方法,将数据值域分割为等宽的若干区间,并统计各个区间的样本数量。
分割变量值域

pythonpandas.cut(x, bins, right=True, labels=None, retbins=False,
precision=3, include_lowest=False, duplicates='raise')#right参数用来调整区间的类型(默认左开右闭)

其中 x 接收要分割的一维数组,bins 接收正整数,表示要分割为的区间数量。
有的时候分割变量也会借助分位数进行分割,这个时候就要用到与 pd.cut()类似的 pd.qcut()方法,若传入 bins 为一个整数,则表示等分的区间个数,若传入的为一个值在 0-1 的列表,则会根据列表进行划分。
统计样本数量,依然还用pd.value_counts()方法函数。

import pandas as pd
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
bins = [18, 25, 35, 60, 100]
cats = pd.cut(ages, bins)
# a special Categorical object
print(cats)

print(cats.categories) # 一个包含所有category名字的对象
print(cats.codes) # 索引值返回
pd.value_counts(cats)
#ch4-3,例4-4 查看汽车销售数据表的数值型特征的描述性统计
import pandas as pd
import numpy as np
path='D:/my_python/ch04/data/'
vs = pd.read_table(path+'vehicle_sales.csv',
sep = ',',encoding = 'gbk')
#例4-8 等宽法离散化数据系列,各区间宽度大致相同,求各区间的频数
k=5#离散化为等宽的k个区间
#返回k个区间,相当于k个类,以bins作为分割点
#返回的amounts变成了series,value变成了区间。
amounts,bins=pd.cut(vs['amounts'],k,retbins=True)
print('销售额分割成',k,'个等宽区间的分割点是:\n',bins)
#统计各个区间记录的频数,**默认按照频数降序排列**
print('汽车销售额等宽离散化为5个区间后每个区间及其频数为:\n',
amounts.value_counts())
#可视化,按照区间从小到大排列
#a_frequency=amounts.value_counts(sort=False)
#可视化,按照频数降序排列
a_frequency=amounts.value_counts()
labels=a_frequency.index[0:k]#将区间作为标签
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = 'SimHei'#设置字体为SimHei显示中文
plt.rc('font', size=14)#设置图中字号大小
plt.figure(figsize=(6,4))#设置画布
plt.bar(range(k),a_frequency)#绘制柱状图
plt.title('销售额等宽法频数统计图')#添加标题
plt.xlabel('销售额')#添加横轴标签
plt.ylabel('频数')#添加y轴名称
plt.xticks(range(k),labels,rotation=20)#横轴刻度与标签对准
plt.show()

等频法离散数据

对于不均匀分布的数据, 有时需要按大致相同的样本频次,观察取得这些频次的样本分布在的不同区间。称为等频法离散化数据,简称为等频法。

  • 将样本从小到大进行排列,按照样本位置将数据划分为位置间隔相等的区间。位置间隔相同意味着样本出现的频数相同。
  • 获得每个区间的第一个和最后一个元素的值,两者的差值即为与该位置区间对应的元素取值区间。
  • 使用 Pandas 的DataFrame.quantile()方法能够获得 DataFrame 的任意分位数,据此可以得到等频的样本值域分割点。
#例4-9 等频法离散化数据,各区间频数大致相同,求区间分割点。
#自定义等频法离散化函数
def same_frequency_cut(data,k):
#产生k个分位数位置
w=data.quantile(np.arange(0,1+1.0/k,1.0/k))
data=pd.cut(data,w)#按照分位数位置离散化data
return data
#返回的a_frequency变成了series,value变成了区间。
a_frequency= same_frequency_cut(vs['amounts'],5).value_counts()
print('汽车销售额等频法离散化后各个类别数目分布状况为:','\n',a_frequency)
#可视化,按照区间从小到大排列
a_frequency=same_frequency_cut(vs['amounts'],5).value_counts(sort=False)
labels=a_frequency.index[0:k]#将区间作为标签
plt.figure(figsize=(6,4))#设置画布
plt.bar(range(k),a_frequency)#绘制柱状图
plt.title('销售额等频法频数统计图')#添加标题
plt.xlabel('销售额')#添加横轴标签
plt.ylabel('频数')#添加y轴名称
plt.xticks(range(k),labels,rotation=20)#横轴刻度与标签对准
plt.show()

pandas 统计分析

DataFrame 描述性统计

单统计量
image.png

  • 极差——Series.ptp(axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
  • 协方差——DataFrame.cov(min_periods=None)
  • 相关系数——DataFrame.corr(method='pearson', min_periods=1)
  • 标准误差——Series.sem(axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
  • 分位数——DataFrame.quantile(q=0.5, axis=0, numeric_only=True, interpolation='linear')
  • 非空值数量——DataFrame.count(axis=0, level=None, numeric_only=False)
  • 平均绝对离差——DataFrame.mad(axis=None, skipna=None, level=None)

这些统计方法都可以用在GroupBy对象后进行分组的描述性统计分析(具体使用方法见分组小节)
多统计量
DataFrame.describe()方法默认返回 DataFrame 全部或指定数值型字段的和、均值、标准差、最小值、最大值、25%分位数、50%分位数和 75%分位数。

DataFrame.describe(percentiles=None, include=None, exclude=None)
# 一般情况下会把结果进行转置,更符合我们的使用习惯
df.describe().T

除此以外可以对其参数进行调整,来对df中其他类型的变量进行统计描述,具体调整细节help即可,这里不再赘述。

DataFrame.describe()函数也可以对 DataFrame 的 category 类型(非数值型)进行描述性统计,返回 count, unique, top,freq 值,分别代表记录的数量、类的数量、记录数量最多的类、记录数量最多的类的记录数量。
当我们用数值来进行分类时,进行统计分析时如果不希望作为类别的数值列也被进行统计分析,可以专门将数值类的列转为非数值型数据(参考综合实例–iris 数据集统计分析代码块第 97 行)。

窗口函数

在实际应用过程中,我们可能会存在对整个 df 的局部数据进行统计分析的场景,这时就需要用到所谓的“窗口函数”,可以理解为在整体数据集上创建窗口来进行运算,pd 中提供的几种窗口函数有:

  • rolling(移动函数)
  • expanding(扩展函数)
  • ewm(指数加权函数)

在数据分析的过程中,使用窗口函数能够提升数据的准确性,并且使数据曲线的变化趋势更加平滑,从而让数据分析变得更加准确、可靠。

先咕咕了

visualizations

Series 和 Df 都有一个 plot 属性来进行基本的一些绘图,默认情况下 se/df.plot()等价于 se/df.plot.line()[绘制线性图],可以通过调节传入参数对绘图进行修饰 plot 属性本身包含许多种绘图方式。
df/se.plot 的方法:

  • line()
  • bar()(index 作为横坐标)
  • barz()(index 作为纵坐标)
  • hist():
  • density():概率密度函数
  • kde():使用 kde 方法进行估计