Pandas 基础

795 阅读29分钟

一、对Pandas库认识

pandas(panel data & data analysis),是基于 numpy(提供高性能的矩阵运算)专门用于数据分析的工具,是一个强大的分析结构化数据(表格数据)的工具集,能够用于数 据挖掘和数据分析,同时也提供数据清洗功能

1、DataFrame

nameagegroup
stu0张某191
stu1王某201
stu2李某192
stu3赵某182

DataFrame 是 Pandas 中的一个表格型的数据结构,包含有一组有序的列,每列可以是 不同的值类型(数值、字符串、布尔型等),观察表格可以发现:表格有三部分组成,表头(列 索引)、行名称(行索引)、数据元素部分,numpy 如何表示表结构的数据,以及和 Dataframe 的区别,numpy 将数据部分、表头(列索引)、行索引单独存放为一个数组

创建 DataFrame 的三种形式

方式 1:逐个传入行索引、列索引和 数据元素

# 导包
import pandas as pd
import numpy as np

# dataframe结构 具有行索引、列索引、数据元素 ---二维数据

# 可以使用pd.DataFrame来创建df数据
# 1、可以将列表嵌套转化为df
df = pd.DataFrame(
    data=[['zs', 17, 182.5],
          ['ls', 18, 190.5],
          ['ww', 16, 173.0]],
    index=['stu_0', 'stu_1', 'stu_2'],
    columns=['name', 'age', 'hight']
)
print('df:\n', df)
print('df类型:\n', type(df))  # <class 'pandas.core.frame.DataFrame'>

方式二:借助字典

# 导包
import pandas as pd
import numpy as np
# 2、可以使用大字典来创建df
df = pd.DataFrame(
    data={
        'name': ['zs', 'ls', 'ww'],
        'age': [17, 18, 16],
        'hight': [173.0, 185.5, 190.0]
    },
    index=['stu0', 'stu1', 'stu2']
)
print('df:\n', df)
print('df:\n', type(df))  # <class 'pandas.core.frame.DataFrame'>

方式三:也可以将二维数组数据转化为DataFrame

# 导包
import pandas as pd
import numpy as np
# 3、也可以将二维数组转化为df
# # 先加载一个二维数组
fp = np.load('./lol_data.npz')
# 遍历获取key
# for k in fp:
#     print(k)
# 获取保存的数组
columns = fp['columns']
data = fp['data']
print('columns:\n', columns)
print('data:\n', data)
# 剔除掉重复的数据
data = np.unique(data, axis=0)
# 先构建一个index
index = ['index_' + str(i) for i in range(data.shape[0])]
print('index:\n', index)

# 转化为df
df = pd.DataFrame(
    data=data,
    columns=columns,
    index=index
)
print('df:\n', df)

2、Series

它是一种类似于一维数组的对象,是由一组数据(各种 NumPy 数据类型)以及一组与之相 关的数据标签(即索引)组成,仅由一组数据也可产生简单的 Series 对象,Series对象没有列索引

stu0张某
stu1王某
stu2李某
stu3赵某

**创建 Series:**可以由简单列表、一维数组转化而来

在 series 对象中,索引与元素之间存在一种映射关系,元素在 series 对象中的有序存 储是通过索引实现的,当传入字典创建 series 对象,可以通过指定索引的方式对 series 对象中的元素进行排序和过滤

# 导包
import pandas as pd
import numpy as np
# 2、通过一维数组、简单列表来转化生产
se = pd.Series(data=['zs', 'ls', 'ww'],index=['stu0', 'stu1', 'stu2'])
se = pd.Series(data=np.array(['zs', 'ls', 'ww']),
               index=['stu0', 'stu1', 'stu2'])
print('se:\n', se)
print('se:\n', type(se))

# 3、也可以通过小字典来生成
se = pd.Series(data={'zs': 79, 'ls': 90, 'ww': 95},
               index=['zs', 'ls', 'ww','zl'])
print('se:\n', se)
print('se:\n', type(se))

Series 和 DataFrame 的关系

获取 dataframe 中的一列数据,得到的数据类型为 Series,DataFrame 可以被看做是由 Series 组成的字典

# Series  ---
# 只有行索引、数据元素 ---没有列索引 -----只能是1维的
# 而且这个一维 指的是行维度
# # 1、获取姓名这一列
se = df['姓名']
print('se:\n', se)
print('se:\n', type(se))  # <class 'pandas.core.series.Series'>

二、DataFrame 属性及 Series 属性

函数返回值备注
values元素表的数据部分
index索引行索引/行名称
columns列名列名称/表头
dtypes元素类型表内元素类型
size元素个数表内元素个数
ndim维度数values
shape数据形状(行列数目)表的数据部分的形状

以一个学生信息表的 DataFrame 为例,来研究 DataFrame 属性

DataFrame 属性

代码实现:

import pandas as pd

# 创建一个df
df = pd.DataFrame(
    data=[['zs', 17, 178.5],
          ['ls', 18, 173],
          ['ww', 19, 178.5]],
    index=['stu0', 'stu1', 'stu2'],
    columns=['name', 'age', 'hight']
)
# 获取DataFrame属性
print('df:\n', df)
print('df的元素个数:\n', df.size)  # --->只计算真实元素,不计算行、列索引
print('df的形状:\n', df.shape)
print('df的维度:\n', df.ndim)

print('df的行索引:\n', df.index)  # 行索引名称
print('df的列索引:\n', df.columns)  # 列索引名称

print('df的数据元素:\n', df.values)
print('df的数据元素:\n', type(df.values))  # <class 'numpy.ndarray'>

print('df的元素类型:\n', df.dtypes)  # 返回的是df中每一列的数据类型

# print('df的元素类型:\n', df.dtype)  # 错误的。因为df可以存储不同的数据类型
# print('df的每个元素的占位大小\n', df.itemsize)  # 错误的,因为df可以存储不同的数据类型

Series 属性

其实 Series 和 DataFrame 的属性相差不大,与 DataFrame 相比,Series 没有 columns 属性,由于只有一列,且属性一致,因此存在 itemsize 属性

代码实现:

import pandas as pd

# 创建一个df
df = pd.DataFrame(
    data=[['zs', 17, 178.5],
          ['ls', 18, 173],
          ['ww', 19, 178.5]],
    index=['stu0', 'stu1', 'stu2'],
    columns=['name', 'age', 'hight']
)

# 获取Series属性
# Series只有行索引,没有列索引
se = df['name']
print('se:\n', se)
print('se:\n', type(se))
print('se 的元素个数:\n', se.size)
print('se 的形状:\n', se.shape)
print('se 的维度:\n', se.ndim)

print('se 的行索引:\n', se.index)
# print('se 的列索引:\n', se.columns)  # 错误的,无列索引

print('se 的数据元素:\n', se.values)
print('se 的数据元素:\n', type(se.values))  # <class 'numpy.ndarray'>

print('se 的元素类型:\n', se.dtypes)
print('se 的元素类型:\n', se.dtype)

# print('se 的元素的占位大小:\n', se.itemsize)  # 最新可能会报错。

三、Pandas 读写文件

通常表数据会存储在文本文件(.txt/.csv 类型)、excel 文件中,需要分析文件中数据时, 利用 Pandas 读取这些文件后会存保存为 DataFrame 数据,此外还可以将处理分析完成的 DataFrame 数据写入文件进行保存,本节主要介绍 pandas 的读写操作

1、文本文件读取

文本文件是一种由若干行字符构成的计算机文件,它是一种典型的顺序文件

csv 是一种特殊的文本文件,默认逗号为分隔符,但其分隔符不一定是逗号,又被称为 字符分隔文件,文件以纯文本形式存储表格数据(数字和文本)

(1)、使用 read_table 来读取文本文件

pandas.read_table(filepath_or_buffer,sep=’\t’,header=’infer’,names=None,index_col=None,dtype=None,engine=None,nrows=None)

(2)、使用 read_csv 函数来读取 csv 文件

pandas.read_csv(filepath_or_buffer,sep=’\t’,header=’infer’,names=None,index_col=None,dtype=None,engine=None,nrows=None)

read_table 和 read_csv 参数说明

参数名称说明
filepath接收 string,代表文件路径,无默认
sep接收 string,代表分隔符,read_csv 默认为",",read_table 默认为制表符"[Tab]"
header接收 int 或者 sequence,表示将某行数据作为列名,默认为 infer,表示自动识别
names接收 array,表示列名,默认为 None
index_col接收 int、sequence 或 False,表示索引列的位置,取值为 sequence 则表示多重索引, 默认为 None
dtypes接收 dict,代表写入的数据类型(列名为 key,数据格式为 values)默认为 None
engine接收 c 或者 python,代表数据解析引擎,默认为 c
nrows接收 int,表示读取前 n 行,默认为 None

注意:

(1)read_table 和 read_csv 函数中的 sep 参数是指定文本的分隔符的,如果分隔符 指定错误,在读取数据的时候,每一行数据将连成一片

(2)header 参数是用来指定列名的,如果是 None 则会添加一个默认的列名

(3)encoding 代表文件的编码格式,常用的编码有 utf-8、utf-16、gbk、gb2312、gb18030 等,如果编码指定错误数据将无法读取,IPython 解释器会报解析错误

代码实现:

import pandas as pd

# pandas读取文件的方式为:pd.read_xxx()

# csv数据 --->默认以逗号分隔的,有序的序列文本数据
# pd.read_table() --pd.read_csv 默认的分隔符就是逗号
# 在编解码的时候,因为中文原因 ---字节 ---utf-8 --->2个字节   gbk--->3个字节
# 编解码必须一致,才会使得中文准确
info = pd.read_csv(filepath_or_buffer='./meal_order_info.csv',  # 文件的路径+名称
                   # encoding='ansi',  # 编码格式
                   # sep=',',   # 分隔符
                   # delimiter=',', # 分隔符
                   # header=0,  # 列索引,header="infer"自动识别,
                   # usecols=['info_id', 'emp_id'],  # 可以指定只读取特定的列
                   # usecols=[0, 1],  # 可以指定只读取特定的列
                   # index_col='info_id',  # 可以通过index_col来指定列作为行索引
                   # names=['col_1','col_2'],  # 可以自定义列索引名称
                   # nrows=10,# 可以指定读取前n行
                   # skiprows=10,# 跳过前n行
                   )
print('info:\n', info)
print('info:\n', type(info))  # <class 'pandas.core.frame.DataFrame'>

2、文本文件保存

文本文件的存储和读取类似,结构化数据可以通过 pandas 中的 to_csv 函数实现以 csv 文件格式存储文件

DataFrame.to_csv(path_or_buf=None,sep=’,’,na_rep=”,columns=None,header=True,index=True,index_label=None,mode=’w’,encoding=None)

to_csv 方法参数说明

参数名称说明
path_or-buf接收 string,代表文件路径,无默认
index接收 boolean,代表是否将行名 写出,默认为 True
sep接收 string,代表分隔符,默认为 ","
index_labels接收 sequence,代表索引名, 默认为 None
na_rep接收 string,代表缺失值,默认为""
mode接收 string,代表数据写入模式。 默认为 w
columns接收 list,代表写出的列名,默认为 None
encoding接收 string,代表存储文件的编 码格式。默认为 None
header接收 boolean,代表是否将列名写 出,默认为 True

代码实现:

# 保存为csv文件
# pandas中保存文件的格式:df.to_xxxx()
# info.to_csv(path_or_buf='./hh.csv',  # 保存的文件和名称,
#             sep=',',  # 分隔符,
#             index=True,  # 保存的时候,需要保存的行索引
#             header=True,  # 保存的时候,需要保存列索引
#             columns=['info_id', 'emp_id'],  # 可以指定保存的列
#             mode="a", # 表示重写,每次都覆盖之前的数据,追加---a
#             )

注意: 默认的保存文件的时候,不管之前文件里面存在什么内容,都将会覆盖原来的内容,并重写,如果想要在保存内容的时候,追加到原来的内容下面,可以通过 mode 参数进行修改, 修改为 mode="a"

3、Excel 文件读取

pandas 提供了 read_excel 函数来读取“xls”、“xlsx”两种 Excel 文件

pandas.read_excel(io, sheetname=0, header=0, index_col=None, names=None, dtype=None)

read_excel 参数说明

参数说明
io接收 string,表示文件路径,无默认
sheetname接收 string、int、None,代表 excel 表内数据的分表位置,默认 0,当 为 None 时,返回该表格中多有 sheet 表格,返回类型为 ordereddict 类型,可以通过 list(返回值.keys())或者 list(返回值)来查看所有 sheet 的 名称
header接收 int 或者 sequence,表示将某行数据作为列名,默认为 infer,表示 自动识别
names接收 int、sequence 或者 False,表示索引列的位置,取值为 sequence 则代表多重索引,默认为 None
index_col接收 int、sequence 或者 False,表示索引列的位置,取值为 sequence 则代表多重索引,默认为 None
dtype接收 dict,代表写入的数据类型(列名为 key,数据格式为 values), 默认为 None

代码实现:

# excel数据 --->以.xls .xlsx为结尾的表格数据
detail = pd.read_excel(io='./meal_order_detail.xlsx',  # 文件的路径+名称
                       # sheet_name=0,  # 默认读取第0个sheet中的数据
                       sheet_name=None,  # 如果为None,则返回OrderedDict对象,包含了所有的sheet
                       # header=0,  # 默认将第0行作为列索引,
                       # names#  可以自定列名称
                       # index_col # 指定某列作为行索引
                       # usecols # 指定读取特定的列
                       # skiprows=None, # 指定跳过n行
                       # nrows=None, # 指定读取n行
                       )
# print('detail:\n',detail)

# 获取所有的sheet 的name
# print(detail.keys())
# 通过key来获取value
detail1 = detail['meal_order_detail1']
detail2 = detail['meal_order_detail2']
detail3 = detail['meal_order_detail3']
# print(detail1.shape)
# print(detail2.shape)
# print(detail3.shape)

4、Excel 文件保存

将文件存储为 Excel 文件,可以使用 to_excel 方法

DataFrame.to_excel(self,excel_writer,sheet_name='Sheet1',na_rep='',float_format=None,columns=None,header=True,index=True,index_label=None,startrow=0,startcol=0,engine=None,merge_cells=True,encoding=None,inf_rep='inf',verbose=True,fre eze_panes=None):

注意: 与 to_csv 方法的常用参数基本一致,区别之处在于指定存储文件的文件路径参数名称为 excel_writer,并且没有 sep 参数,增加了一个 sheetnames 参数用来指定存储的 Excel sheet的名称,默认为 sheet1

代码实现: 以订单详情信息为例,保存读取出来的订单详情信息

# 将数据保存为 .xlsx文件
# detail1.to_excel(excel_writer='./hh.xlsx', sheet_name='hh')
# detail2.to_excel(excel_writer='./hh.xlsx', sheet_name='gg')

# 将多个df保存到一个excel的不同sheet中
# 借助 ExcelWriter 来讲多个df保存到一个excel的不同sheet
writer = pd.ExcelWriter('./hh.xlsx')
# 将不同的df保存到writer
detail1.to_excel(excel_writer=writer, sheet_name='hh')
detail2.to_excel(excel_writer=writer, sheet_name='gg')
detail3.to_excel(excel_writer=writer, sheet_name='kk')
# 保存writer
writer.save()
# 关闭writer
writer.close()

注意:

(1)如 果 报 : ImportError: No module named 'openpyxl' 错 误 , 请 : pip install openpyxl

(2)如果保存的时候指定 sheetnames 参数,会发现,每保存一次,会覆盖一次保存的 内容,即只会保留最后一次保存的内容。如果想要将不同的 dataframe 保存到同一个 excel 的不同 sheet 中去可以:

四、DataFrame 的查询操作

1、直接索引方式

对单列数据的访问:DataFrame 的单列数据为一个 Series,根据 DataFrame 的定义可以知晓 DataFrame 是一个带有标签的二维数组,每个标签相当每一列的列名,有以下两种方式 来实现对单列数据的访问

(1)以字典访问某一个 key 的值的方式使用对应的列名,实现单列数据的访问

(2)以属性的方式访问,实现单列数据的访问,(不建议使用,易引起混淆) 以 菜 品 订 单 为 例 , 使 用 字 典 访 问 内 部 数 据 的 方 式 访 问 DataFrame 的 单 列 数 (dishes_name 列)的数据

以 菜 品 订 单 为 例 , 使 用 字 典 访 问 内 部 数 据 的 方 式 访 问 DataFrame 的单列数(dishes_name 列)的数据

import pandas as pd

detail = pd.read_excel('./meal_order_detail.xlsx', sheet_name=0)
print('detail:\n', detail)
print('detail:\n', detail.columns)
print('*' * 100)

# 对Index 进行更改
index = ['index_' + str(tmp) for tmp in range(detail.shape[0])]
print('index:\n', index)

#
detail.index = index

# 直接获取数据方式  ---->先列后行

# 获取单列数据 --->获取菜品名称
print('获取单列数据:\n', detail['dishes_name'])

对某一列的某几行访问:访问 DataFrame 中某一列的某几行时,单独一列的 DataFrame 可以视为一个Series(另一种 pandas 提供的类,可以看作是只有一列的 DataFrame),而 访问一个 Series 基本和访问 一个一维的 ndarray 相同

以 菜 品 订 单 为 例 , 使 用 字 典 访 问 内 部 数 据 的 方 式 访 问 DataFrame 的单列数 (dishes_name 列)的 n 行数据

# 单列数据获取指定行 ----行下标切片,行下标列表,行名称列表,行名称切片,head()  tail()

# # 获取单列数据的前n行  ---行下标切片
print('获取单列数据的前n行:\n', detail['dishes_name'][:10])

# # 获取单列数据的前n行  ---行名称切片(包含结束位置)
print('获取单列数据的前n行:\n', detail['dishes_name']['index_0':'index_10'])

# # 获取单列数据的前n行  --- 行下标列表
print('获取单列数据的前n行:\n', detail['dishes_name'][[0, 1, 2, 3, 4, 5, 6]])

# # 获取单列数据的前n行  --- 行名称列表
print('获取单列数据的前n行:\n', detail['dishes_name'][['index_0', 'index_1', 'index_10']])

# # 获取单列数据的前n行  --- head()  默认获取前5行
print('获取单列数据的前n行:\n', detail['dishes_name'].head(10))

# 获取单列数据的后n行 ---tail() 默认获取后5行
print('获取单列数据的前n行:\n', detail['dishes_name'].tail(10))

对多列数据访问:访问 DataFrame 多列数据可以将多个列索引名称视为一个列表,同时 访问 DataFrame 多列数据中的多行数据

以菜品订单为例,访问 DataFrame 的多列数(dishes_name、counts、amounts 列)的数据

# 获取多列数据 --->获取菜品id  和 菜品名称
print('获取多列数据:\n',detail[['dishes_name','dishes_id']])

以菜品订单为例,访问 DataFrame 的多列数(dishes_name、counts、amounts 列)的 n 行数据

# # 获取多列数据的前n行  ---行下标切片,行名称切片,head() tail()
print('获取多列数据:\n', detail[['dishes_name', 'dishes_id']][:10])
print('获取多列数据:\n', detail[['dishes_name', 'dishes_id']]['index_0':'index_10'])
print('获取多列数据:\n', detail[['dishes_name', 'dishes_id']].head(10))
print('获取多列数据:\n', detail[['dishes_name', 'dishes_id']].tail(10))
# print('获取多列数据:\n', detail[['dishes_name', 'dishes_id']][[0, 1, 2, 3]])  # 错误的
# print('获取多列数据:\n', detail[['dishes_name', 'dishes_id']][['index_0', 'index_1']])  # 错误的

2、loc 与 iloc 索引方式

loc 方法是针对 DataFrame 索引名称的切片方法,如果传入的不是索引名称,那么切片 操作将无法执行。利用 loc 方法,能够实现所有单层索引切片操作

loc 方法使用方法如下: DataFrame.loc[行索引名称或条件, 列索引名称]

iloc 和 loc 区别是 iloc 接收的必须是行索引和列索引的位置

iloc 方法的使用方法如下: DataFrame.iloc[行索引位置, 列索引位置]

使用 loc 方法和 iloc 实现多列切片,其原理的通俗解释就是将多列的列名或者位置作为一个列表或者数据传入

使用 loc,iloc 方法可以取出 DataFrame 中的任意数据

在 loc 使用的时候内部传入的行索引名称如果为一个区间,则前后均为闭区间,iloc 方法使用时内部传入的行索引位置或列索引位置为区间时,则为前闭后开区间

loc 内部还可以传入表达式,结果会返回满足表达式的所有值,以订单详情为例,以 loc、iloc 方式获取 DataFrame 中的多列元素

import pandas as pd

# 加载数据
detail = pd.read_excel('./meal_order_detail.xlsx', sheet_name=0)
print('detail:\n', detail)
print('detail:\n', detail.columns)

# 直接索引方式 ---先列后行

# 同时索引----->借助loc iloc方法来进行

# 在loc中---只能使用名称,不能使用下标

# 获取单列数据
print('获取单列数据:\n', detail.loc[:, 'dishes_name'])

# 获取多列数据
print('获取多列数据:\n', detail.loc[:, ['dishes_name', 'dishes_id']])

# 获取多列数据
print('获取多列数据:\n', detail.loc[:, 'detail_id':'dishes_name'])
# print('获取多列数据:\n', detail['detail_id':'dishes_name'])  # 错误的,直接索引方式获取多列数据只能使用名称列表

# 获取多列数据的前n行
print('获取多列数据的前n行:\n', detail.loc[0:3, ['dishes_name', 'dishes_id']])

# print('获取多列数据:\n', detail.loc[:, [0, 1, 2]])  # 错误的,loc只能使用名称,不能使用下标

# iloc与loc都属于同时索引,iloc只能使用下标,不能使用名称
print('获取多列数据的前n行:\n', detail.iloc[0:3, 0:3])  # 行列上都使用的是下标

print('获取单列数据:\n', detail.iloc[:, 5])

# print('获取单列数据:\n', detail.iloc[:, 'dishes_name']) # 错误的,iloc只能使用下标,不能使用名称


# 直接索引的方式速度最快
# loc与iloc一般封装于大平台

loc 更加灵活多变,代码的可读性更高,iloc 的代码简洁,但可读性不高。具体在数据分析 工作中使用哪一种方法,根据情况而定,大多数时候建议使用 loc 方法

五、DataFrame 的增删改操作

1、DataFrame 的增加

DataFrame 添加一列的方法非常简单,只需要新建一个列索引。并对该索引下的数据进行赋值操作即可

新增的一列值是相同的则直接赋值一个常量即可

以学生信息表为例,给 DataFrame 中增加一列

DataFrame 添加一行,需要给各个属性赋值

代码实现:

import pandas as pd

# 加载数据
users = pd.read_excel('./users.xlsx', sheet_name=0)
print('users:\n', users)
print('users:\n', users.columns)
print('*' * 100)

# 给users 新增加一列
# 增加新列的格式: df['新列名'] = 值 或者 df.loc[:,'新列名'] = 值

users['next_year_age'] = 19
print('users:\n', users)


#  ---> 新的列 数据 整列都一样  --->在该列上无法区分不同数据对象的区别

# 在已存在的数据中进行各种运算,然后将运算的结果 作为新列的数据
users.loc[:, 'next_year_age'] = users['age'] + 1
print('users:\n', users)

# 增加一列 意味着:给已存在的数据对象添加新的属性
# 增加一行 意味着:增加新的数据对象

2、DataFrame 的修改

更改 DataFrame 中的数据,原理是将这部分数据提取出来,重新赋值为新的数据

需要注意的是,数据更改直接针对 DataFrame 原数据更改,操作无法撤销,改,需要对更改条件做确认或对数据进行备份

以学生信息表为例,将所有学生的 age 改为 18 岁

**注意:**如果重新直接赋值,会将整列数据改为一样的,但是在真实的环境中,并不是这 样去更改数据,而是,将满足某些条件的情况下,才对数据进行修改,那么如何实现呢?此 时就用到我们之前学到的 bool 数组索引方式

以学生信息表为例,将所有用户 age 为偶数的 age 改为 18 岁

代码实现:

import pandas as pd
import numpy as np

# 加载数据
users = pd.read_excel('./users.xlsx', sheet_name=0)
print('users:\n', users)
print('users:\n', users.columns)

# 修改格式:df['已存在的列名'] = 值

# 将 users 修改为 18
users['age'] = 18
print('users:\n', users)

# ---->整列数据相同  --->在该列上无法区分不同数据对象的区别

# 修改满足条件的数据
# 条件:将年龄为偶数的值  ---->修改为18岁
# 1、写个循环判断
# 2、np.where(条件,值1,值2)  ---> 如果满足条件,将值设置为值1,如果不满足条件,设置为值2
users['age'] = np.where(users['age'] % 2 == 0, 18, users['age'])
print('users:\n', users)
# 3、bool数组索引 + 修改操作
# 确定bool数组
mask = users['age'] % 2 == 0
# 修改
users.loc[mask, 'age'] = 18

# 条件:将 sex 为 女 的 年龄 修改为 18岁
# 确定bool数组
mask = users['sex'] == '女'
# 修改
users.loc[mask, 'age'] = 18

# 条件:将 年龄 大于30岁 的人的 address 修改为中国
# 确定bool数组
mask = users['age'] > 30
# 修改
users.loc[mask, 'address'] = 'China'
#
print('users:\n', users)

3、DataFrame 的删除

删除某列或某行数据需要用到 pandas 提供的方法 drop

drop 方法的用法如下:

axis 为 0 时表示删除行,axis 为 1 时表示删除列

drop(labels, axis=0, level=None, inplace=False, errors='raise')

常用参数如下所示:

参数说明
labels接收 string 或 array,代表删除的行或者列的标签,无默认
axis接收 0 或者 1,代表操作的轴向,默认为 0
inplace接收 boolean,代表操作是否对原数据生效,默认为 False
import pandas as pd

# 加载数据
users = pd.read_excel('./users.xlsx', sheet_name=0)
print('users:\n', users)
print('users:\n', users.columns)

# 格式:df.drop()方法来删除 指定行、列

# 删除users里面的 'sex', 'poo' ,'address', 'age'
# labels -->删除的列、行的名称列表
# axis --->轴的方向,如果删除的是列,axis=1,如果删除的是行,axis=0
# inplace
# 如果为True,直接对原df进行修改,
# 如果为False,不会对原df产生修改,会返回一个删除之后的结果df
users.drop(labels=['sex', 'poo', 'address', 'age'],
           axis=1,
           inplace=True)
print('删除之后的结果:\n', users.shape)

users.drop(labels=[0, 1, 2, 3, 4, 5, 6, 7],
           axis=0,
           inplace=True)

print('删除之后的结果:\n', users.shape)

# 删除某些条件下的数据

# # 条件:删除 sex为女的行
# # 确定bool数组
# mask = users.loc[:, 'sex'] == '女'
# # 确定删除的行、列名称列表
# labels = users.loc[mask, :].index
# # 删除
# users.drop(labels=labels, axis=0, inplace=True)
# print('删除之后的结果:\n', users.shape)

# # 条件: 删除 age 为 奇数的 行
# # 确定bool数组
# mask = users.loc[:, 'age'] % 2 != 0
# # 确定删除的行的名称列表
# labels = users.loc[mask, :].index
# # 删除
# users.drop(labels=labels, axis=0, inplace=True)
# print('删除之后的结果:\n', users.shape)

# 保留法
# 删除 sex为女的行  -----> 保留 sex 不为女 的行
# # 1、确定bool数组
# mask = users.loc[:, 'sex'] != '女'
# # 2、选中 True
# users = users.loc[mask, :]
# print('保留的结果:\n', users.shape)

# # 删除 age 为奇数的行  -----> 保留 age 不为奇数的行
# # 1、确定不为奇数的行的 bool数组
# mask = users.loc[:, 'age'] % 2 == 0
# # 2、选中True
# users = users.loc[mask, :]
# print('保留的结果:\n', users.shape)

六、Pandas 的统计分析

1、NumPy 中的统计学函数

数值型数据的描述性统计主要包括了计算数值型数据的完整情况、最小值、均值、中位 数、最大值、四分位数、极差、标准差、方差、协方差和变异系数等

在 NumPy 库中一些常用的统计学函数如下表所示:

函数说明函数说明
np.min最小值np.median中位数
np.max最大值np.std标准差
np.mean均值np.var方差
np.ptp极差np.cov协方差

2、Pandas 库基于 NumPy,自然也可以用这些函数对数据框进行描述性统计

函数说明函数说明
min最小值max最大值
mean均值ptp极差
median中位数std标准差
var方差mode众数
quantile四分位数count非空值数目
describe描述统计

Pandas 提供了一个方法叫作 describe,能够一次性得出数据框所有数值型特征的非空 值数目、均值、四分位数、标准差

类别型特征的描述性统计:

描述类别型特征的分布状况,可以使用频数统计表,Pandas 库中实现频数统计的方法 为 value_counts,该方法是一种查看表格某列中有多少个不同值的快捷方法,并计算每个 不同值有在该列中有多少重复值,仅 Series 存在 value_counts 方法,DataFrame 中不存该方法

Pandas 提供了 categories 类,可以使用 astype 方法将目标特征的数据类型转换为 category 类别

describe 方法除了支持传统数值型以外,还能够支持对 category 类型的数据进行描述 性统计,四个统计量分别为列非空元素的数目,类别的数目,数目最多的类别,数目最多类别的数目

代码实现:

import pandas as pd
import numpy as np

# numpy中的统计指标
# sum  max  min   mean  std var  argmax argmin  cumsum cumprod
# 针对数值类型

# pandas既可以对数值类型进行统计指标、也可以对非数值型数据进行统计指标

# 加载数据
detail = pd.read_excel('./meal_order_detail.xlsx', sheet_name=0)
print('detail:\n', detail)
print('detail:\n', detail.columns)

# 数值型数据
# 以counts amounts为例来进行统计
print('对counts 、 amounts 进行求取 最小值:\n', detail.loc[:, ['counts', 'amounts']].min())

print('求取 amounts 列 的最小值:\n', detail.loc[:, 'amounts'].min())

print('求取 amounts 列 的最大值:\n', detail.loc[:, 'amounts'].max())

print('求取 amounts 列 的均值:\n', detail.loc[:, 'amounts'].mean())

print('求取 amounts 列 的中位数:\n', detail.loc[:, 'amounts'].median())

# 极差 = 极大值 - 极小值
print('求取 amounts 列极差:\n', detail.loc[:, 'amounts'].ptp())  # 未来会被删除,可以使用numpy.ptp来代替

# 标准差
print('求取 amounts 列标准差:\n', detail.loc[:, 'amounts'].std())

print('求取 amounts 列方差:\n', detail.loc[:, 'amounts'].var())

# count ---非空数据的元素个数
print('求取 amounts 列 count:\n', detail.loc[:, 'amounts'].count())
print('求取 cost列 count :\n', detail.loc[:, 'cost'].count())

# mode ---众数 ---出现次数最多的数  ---众数可能存在多个
#  返回值为 series,如果想要获取到具体的众数的值,那么需要使用行下标、行名称
print('求取 amounts列 众数:\n', detail.loc[:, 'amounts'].mode())

# 四分位数
# 默认获取的是中位数,
# 通过给参数q传递值,来获取不同位置的分位数
print('求取 amounts 列的四分位数:\n', detail.loc[:, 'amounts'].quantile(q=np.arange(0, 1 + 1 / 4, 1 / 4)))

# describe统计描述
# 返回8种结果
# # 非空数据数目、均值、标准差、四分位数指标
print('对 amounts 列进行统计描述:\n', detail.loc[:, 'amounts'].describe())

# 非数值型数据
# 返回4种结果:非空数据的数目、去重之后的数目、众数、众数出现的次数
print('对于 dishes_name 列进行统计描述:\n', detail.loc[:, 'dishes_name'].describe())

# 如果不确定某列数据为非数值型,但是我们想要的非数值型的统计描述,
# 先将数据明确转化为非数值型  ---> category --类别型类型
# 可以通过astype来修改数据类型
detail.loc[:, 'dishes_name'] = detail.loc[:, 'dishes_name'].astype('category')

print('查看数据类型:', detail.loc[:, 'dishes_name'].dtypes)

print('对于 dishes_name 列进行统计描述:\n', detail.loc[:, 'dishes_name'].describe())

print('对于 dishes_name 统计众数:\n', detail.loc[:, 'dishes_name'].mode())

七、案例:统计最火菜品案例

在学习完 Pandas 统计分析的情况下,以订单详情数据为例,我们可以统计一下该店铺 中最火的菜品是什么?而且这个最火的菜品总共出现了多少次?这个对于店铺来说,就可以 以该菜品作为折扣菜品来吸引更多的用户、或者可以用以作为招牌菜品来推广

以下为订单详情信息表:

代码实现:

import pandas as pd

# 以detail为例,该餐厅的最火菜品及该菜品出现的次数

# 对菜品名称 统计其众数 以及 众数出现的次数

detail = pd.read_excel('./meal_order_detail.xlsx', sheet_name=0)
print('detail:\n', detail)
print('detail:\n', detail.columns)

# 认为  白饭/大碗  不算菜品

# 1、确定bool数组
mask = detail.loc[:, 'dishes_name'] == '白饭/大碗'
# 2、确定白饭/大碗 的行名称
drop_labels = detail.loc[mask, :].index
# 3、删除
detail.drop(labels=drop_labels, axis=0, inplace=True)

# 也可以使用保留法 ---保留非白饭/大碗的数据
print('剔除完主食之后的订单信息为:', detail.shape)

#
print('最火菜品:\n', detail.loc[:, 'dishes_name'].describe()['top'])
print('最火菜品出现的次数:\n', detail.loc[:, 'dishes_name'].describe()['freq'])

# 保留法

mask = detail.loc[:, 'dishes_name'] != '白饭/大碗'

drop_labels = detail.loc[mask, :]
#
print('最火菜品:\n', drop_labels.loc[:, 'dishes_name'].describe()['top'])
print('最火菜品出现的次数:\n', drop_labels.loc[:, 'dishes_name'].describe()['freq'])

八、案例:菜品数据缺失值处理

在订单详情数据统计的时候,存在整列为缺失的情况,那么整列缺失的该列特征对于数 据分析的结果是毫无意义的,那么我们可以删除掉这种特征

代码实现:

import pandas as pd

# 以detail为例,如果整列数据为空, --->该列对于数据分析结果无意义 --->干掉
# 删除掉detail中整列为空的数据

detail = pd.read_excel('./meal_order_detail.xlsx', sheet_name=0)
print('detail:\n', detail)
print('detail:\n', detail.columns)

# 构建一个list
drop_labels = []

# 需要判断每一列是否都为空
for column in detail.columns:
    # 判读对应的列
    if detail.loc[:, column].count() == 0:
        drop_labels.append(column)

# 打印结果
print('获取到的整列为空的都有:', drop_labels)
print('获取到的整列为空的都有:', len(drop_labels))

# 删除全部为空的列
detail.drop(labels=drop_labels, axis=1, inplace=True)

print('删除整列全部为空的数据:\n', detail.shape)

九、Pandas 的时间数据

在多数情况下,对时间类型数据进行分析的前提就是将原本为字符串的时间转换为标准时间类型

Pandas 继承了 NumPy 库和 datetime 库的时间相关模块,提供了 6 种时间相关的类

Pandas 中的时间模块说明

类名称说明
Timestamp最基础的时间类,表示某个时间点,在绝大多数场景中的时间数据都是 Timestamp 形式的时间
Timedelta表示不同单位的时间,例如 1 天,1.5 小时,3 分钟,4 秒等,而非具体的某个 是时间段
DatetimeIndex一组 Timestamp 构成的 Index,可以用来作为 Series 或者 DataFrame 的索引
TimedeltaIndex一组 Timedelta 构成的 Index,可以迎来作为 Series 或者 DataFrame 的索引

1、Timestamp 时间类型

其中 Timestamp 作为时间类中最基础的,也是最为常用的,在多数情况下,时间相关的字符串都会转换成为 Timestamp,pandas 提供了 to_datetime 函数,能够实现这一目标

代码实现:

import pandas as pd

# Timestamp : pandas中默认支持的时间点类型
# DatetimeIndex : pandas中默认支持的时间序列类型
# datetime64[ns] : numpy中的时间类型

# '2020-08-04' --->str
res = pd.to_datetime('2020-08-04')
print('res:\n', res)
print('res:\n', type(res))

# ['2020-08-01','2020-08-02','2020-08-03','2020-08-04']  --->时间字符串的序列
#
res = pd.to_datetime(['2020-08-01', '2020-08-02', '2020-08-03', '2020-08-04'])
res1 = pd.DatetimeIndex(['2020-08-01', '2020-08-02', '2020-08-03', '2020-08-04'])
print('res:\n', res)
print('res:\n', type(res))
print('res:\n', res1)
print('res:\n', type(res1))

在多数涉及时间相关的数据处理,统计分析的过程中,需要提取时间中的年份,月份等数据,就可以使用对应的 Timestamp 类属性就能够实现这一目的

结合 Python 列表推导式,可以实现对 DataFrame 某一列时间信息数据的提取

时间属性说明表

属性说明属性说明
yearweek一年中第几周
monthquarter季度
dayweekofyear一年中第几周
hourdayofyear一年中的第几天
minutedayofweek一周第几天
secondweekday一周第几天
date日期Weekday_name星期名称
time时间Is_leap_year是否闰年

代码实现:

# 加载数据
detail = pd.read_excel('./meal_order_detail.xlsx', sheet_name=0)
print('detail:\n', detail)
print('detail:\n', detail.columns)
# 先将 place_order_time 转化为pandas 默认支持的时间序列
detail.loc[:, 'place_order_time'] = pd.to_datetime(detail.loc[:, 'place_order_time'])

# 获取时间数据中的时间属性
# 列表推导式获取
year = [tmp.year for tmp in detail.loc[:, 'place_order_time']]
print('year:\n', year)

# 加入到 新的列
detail['year'] = [tmp.year for tmp in detail.loc[:, 'place_order_time']]
print('detail:\n', detail)


month = [tmp.month for tmp in detail.loc[:, 'place_order_time']]
print('month:\n', month)

day = [tmp.day for tmp in detail.loc[:, 'place_order_time']]
print('day:\n', day)

weekday_name = [tmp.day_name() for tmp in detail.loc[:, 'place_order_time']]
print('weekday_name:\n', weekday_name)

# 获取日期
date = [tmp.date() for tmp in detail.loc[:, 'place_order_time']]
print('date:\n', date)

# 获取时间
time = [tmp.time() for tmp in detail.loc[:, 'place_order_time']]
print('time:\n', time)

# dt --方式
# 获取年月日
year = detail.loc[:,'place_order_time'].dt.year
print('year:\n', year)


month = detail.loc[:,'place_order_time'].dt.month
print('moth:\n', month)

time = detail.loc[:,'place_order_time'].dt.time
print('time:\n', time)

date = detail.loc[:,'place_order_time'].dt.date
print('date:\n', date)

2、Timedelta 时间类型

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

Timedelta 中周期、单位、及说明对照表

周期名称单位说明周期名称单位说明
weeks星期secondss
daysDmillisecondsms毫秒
hoursh小时microsecondsus微秒
minutesmnanosecondsns纳秒

使用 Timedelta ,可以很轻松地实现在某个时间上加减一段时间

除了使用 Timedelta 实现时间的平移外,还能够直接对两个时间序列进行相减,从而得 出一个 Timedelta

代码实现:

# 时间的相加减
# Timedelta :关于时间差的计算
res = pd.to_datetime('2020-08-04') - pd.to_datetime('2020-08-01')
print('res:\n', res)  # 3 days 00:00:00
print('res:\n', type(res))  # Timedelta
print(res.days)
print(dir(res))
print(res.seconds)

# 时间推移
# [weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds]
res = pd.to_datetime('2020-02-25') - pd.Timedelta(years=5)
print('res:\n',res)
print('res:\n',type(res))

# 时间序列创建
# 参数:start --开始时间
# 参数:end ---结束时间
# 参数: periods ---时长
# 参数:freq --频率
res = pd.date_range(start='2020-08-04', end='2020-10-01', freq='36D')
res1 = pd.date_range(start='2020-08-04', end='2020-10-01', freq='W')
res2 = pd.date_range(start='2020-08-04', end='2020-10-01', freq='M')
res3 = pd.date_range(start='2020-08-04', periods=5, freq='M')
print('res:\n', res)
print('res:\n', res1)
print('res:\n', res2)
print('res:\n', res3)

# 获取本机中有效时间区间
print(pd.Timestamp.min)
print(pd.Timestamp.max)