pandas-note

发布时间 2023-06-28 12:52:41作者: 心若向阳~

Table of Contents

pandas 数据分析库

第一部分 课程介绍

  • Python在数据处理和准备方面一直做得很好,但在数据分析和建模方面就差一些。pandas帮助填补了这一空白,使您能够在Python中执行整个数据分析工作流程,而不必切换到更特定于领域的语言,如R。
  • 与出色的 jupyter工具包和其他库相结合,Python中用于进行数据分析的环境在性能、生产率和协作能力方面都是卓越的。
  • pandas是 Python 的核心数据分析支持库,提供了快速、灵活、明确的数据结构,旨在简单、直观地处理关系型、标记型数据。pandas是Python进行数据分析的必备高级工具。
  • pandas的主要数据结构是 Series(一维数据)与 **DataFrame **(二维数据),这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大多数案例
  • 处理数据一般分为几个阶段:数据整理与清洗、数据分析与建模、数据可视化与制表,Pandas 是处理数据的理想工具。
  • pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple

pandas库的亮点

  • 一个快速、高效的DataFrame对象,用于数据操作和综合索引;
  • 用于在内存数据结构和不同格式之间读写数据的工具:CSV和文本文件、Microsoft Excel、SQL数据库和快速HDF 5格式;
  • 智能数据对齐和丢失数据的综合处理:在计算中获得基于标签的自动对齐,并轻松地将凌乱的数据操作为有序的形式;
  • 数据集的灵活调整和旋转;
  • 基于智能标签的切片、花式索引和大型数据集的子集
  • 可以从数据结构中插入和删除列,以实现大小可变
  • 通过在强大的引擎中聚合或转换数据,允许对数据集进行拆分应用组合操作;
  • 数据集的高性能合并和连接
  • 层次轴索引提供了在低维数据结构中处理高维数据的直观方法;
  • 时间序列-功能:日期范围生成和频率转换、移动窗口统计、移动窗口线性回归、日期转换和滞后。甚至在不丢失数据的情况下创建特定领域的时间偏移和加入时间序列;
  • 性能进行了高度优化,用Cython或C编写了关键代码路径。
  • Python与pandas在广泛的学术和商业领域中使用,包括金融,神经科学,经济学,统计学,广告,网络分析,等等
  • 学到这里,体会一会pandas库的亮点,如果对哪些还不熟悉,请对之前知识点再次进行复习。

第二部分 数据结构

第一节 Series - 一维数据

用列表生成 Series时,Pandas 默认自动生成整数索引,也可以指定索引M

# Series - 一维数据
# 用列表生成 Series时,Pandas 默认自动生成整数索引,也可以指定索引
import pandas as pd

# 指定索引 - 适合用来代替字典
s1 = pd.Series(data = [0,3,5,7],index=['a','b','c','d'])
display(s1,s1.values,s1.index) # 取数据 values, 取索引 index

# 不指定索引 - 默认从0开始的索引
s2 = pd.Series(data = [0,3,5,7])
display(s2)

# 字典转Series
d = {'a':1,'b':2,'c':3}
s3 = pd.Series(data=d)
display(s3)

第二节 DataFrame - 二维数组

DataFrame是由多种类型的列构成的二维标签数据结构,类似于 Excel 、SQL 表,或 Series 对象构成的字典。

# 第二节 DataFrame - 二维数组
# DataFrame是由多种类型的列构成的二维标签数据结构,
# 类似于 Excel 、SQL 表,或 Series 对象构成的字典。
import pandas as pd
import numpy as np

# 不指定索引 - 默认从0开始的索引
df1 = pd.DataFrame(data=np.random.randint(0,150,size=(4,3)))
display(df1)

# 指定索引 - 推荐使用
df2 = pd.DataFrame(data=np.random.randint(0,150,size=(4,3)),
                   columns=['python','java','scala'], # 列索引
                   index=list('AbCd'))# 行索引
display(df2)

# 根据字典创建 - 字典中的key作为列索引,vaule 作为列数据 - 以列表的形式返回
df3 = pd.DataFrame(data = {'python':np.random.randint(0,150,size=(4)),
                     'java':np.random.randint(0,150,size=(4)),
                     'scala':np.random.randint(0,150,size=(4))}, 
                       index=np.arange(1,5), # index自增
                   dtype=np.float64)
display(df3)

第三部分 数据查看

查看DataFrame的常用属性和DataFrame的概览和统计信息

head/tail

import numpy as np
import pandas as pd
# 创建 shape(150,3)的二维标签数组结构DataFrame
df = pd.DataFrame(data = np.random.randint(0,151,size = (150,3)),
                   index = None,# 行索引默认
                   columns=['Python','Math','En'])# 列索引

# head 
r1 = df.head(3) # 显示头部3行,默认5个
# tail
r2 = df.tail(3) # 显示末尾3行,默认5个

display(r1,r2)

shape/dtypes - 数据形状/数据类型

import numpy as np
import pandas as pd
# 创建 shape(150,3)的二维标签数组结构DataFrame
df = pd.DataFrame(data = np.random.randint(0,151,size = (150,3)),
                   index = None,# 行索引默认
                   columns=['Python','Math','En'])# 列索引

# shape  - 数据形状
r1 = df.shape   # (150, 3)
print('数据形状:')
display(r1)
# dtypes - 数据类型
r2 = df.dtypes 
print('数据类型:')
display(r2)

index/columns/values - 查看索引 - 行/列/属性

import numpy as np
import pandas as pd
# 创建 shape(150,3)的二维标签数组结构DataFrame
df = pd.DataFrame(data = np.random.randint(0,151,size = (150,3)),
                   index = None,# 行索引默认
                   columns=['Python','Math','En'])# 列索引

# 行索引  - index - 列表
r1 = df.index 
print('行索引:')
display(r1)

# 列索引 - columns - 列表
r2 = df.columns 
print('列索引:')
display(r2)
# 对象值,二维ndarray数组
r3 = df.values.copy()
print('属性值:')
display(r3)

describe/info - 查看数据信息 - 重要

# 查看其属性、概览和统计信息
import numpy as np
import pandas as pd
# 创建 shape(150,3)的二维标签数组结构DataFrame
df = pd.DataFrame(data = np.random.randint(0,151,size = (150,3)),
                   index = None,# 行索引默认
                   columns=['Python','Math','En'])# 列索引



# 查看数值型列的汇总统计,计数、平均值、标准差、最小值、四分位数、最大值
r1 = df.describe() 
print('查看数值型列的汇总统计:')
display(r1)

# 查看列索引、数据类型、非空计数和内存信息
print('查看列索引、数据类型、非空计数和内存信息:')
df.info()

补充 - isnull

# 主要用来判断每列数据,是否有空数据
import numpy as np
import pandas as pd
# 创建 shape(150,3)的二维标签数组结构DataFrame
df = pd.DataFrame(data = np.random.randint(0,151,size = (150,3)),
                   index = None,# 行索引默认
                   columns=['Python','Math','En'])# 列索引
df.iloc[1,1] = np.NaN
df.isnull().sum()

第四部分 数据的输入输出

第一节 csv文件

import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,50,size = [50,5]), # 薪资情况
               columns=['IT','化工','生物','教师','士兵'],index=np.arange(1,51))


# 保存到当前路径下,文件命名是:salary.csv。csv逗号分割值文件格式
df.to_csv('data/salary.csv',
          sep = ';', # 文本分隔符,默认是逗号
          header = True,# 是否保存列索引
          index = True) # 是否保存行索引,保存行索引,文件被加载时,默认行索引会作为一列
          # 这里一般 index = False 不设索引会比较好


# 读取数据 - read_csv
data1 = pd.read_csv('data/salary.csv',
            sep = ';',# 默认是逗号 - 不是逗号分割要指定分隔符
            header = [0],#指定列索引
            index_col=0) # 指定行索引
display(data1.head(5))

# 读取数据 - read_table
data2= pd.read_table('data/salary.csv', # 和read_csv类似,读取限定分隔符的文本文件
            sep = ';',
            header = [0],#指定列索引
            index_col=1,) # 指定行索引,IT作为行索引

display(data2.head(5))

第二节 Excel文件

pip install xlrd -i https://pypi.tuna.tsinghua.edu.cn/simple

pip install xlwt -i https://pypi.tuna.tsinghua.edu.cn/simple

import numpy as np
import pandas as pd
#注意: As the xlwt package is no longer maintained, 
# the xlwt engine will be removed in a future version of pandas.
# 解决:将xls 文件后缀改为 xlsx

df1 = pd.DataFrame(data = np.random.randint(0,50,size = [50,5]), # 薪资情况
               columns=['IT','化工','生物','教师','士兵'])
df2 = pd.DataFrame(data = np.random.randint(0,50,size = [150,3]),# 计算机科目的考试成绩
                   columns=['Python','Tensorflow','Keras'])



# 保存到当前路径下,文件命名是:salary.xls
df1.to_excel('data/salary.xlsx',
            sheet_name = 'salary',# Excel中工作表的名字,默认为第一个
            header = True,# 是否保存列索引
            index = False) # 是否保存行索引,保存行索引

data1 = pd.read_excel('data/salary.xlsx',
              sheet_name=0,# 读取哪一个Excel中工作表,默认第一个
              header = 0,# 使用第一行数据作为列索引
              names = list('ABCDE'),# 替换行索引
              index_col=1)# 指定行索引,B作为行索引
display(data1.head(5))



# 一个Excel文件中保存多个工作表 
with pd.ExcelWriter('data/data.xlsx') as writer:
    df1.to_excel(writer,sheet_name='salary',index = False)
    df2.to_excel(writer,sheet_name='score',index = False)
    
    
data2 = pd.read_excel('data/data.xlsx',
              sheet_name='score') # 读取Excel中指定名字的工作表 

display(data2.head(5))

第三节 SQL - 数据库

pip install sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple

pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple

import pandas as pd
# SQLAlchemy是Python编程语言下的一款开源软件。提供了SQL工具包及对象关系映射(ORM)工具
from sqlalchemy import create_engine

df = pd.DataFrame(data = np.random.randint(0,50,size = [150,3]),# 计算机科目的考试成绩
                   columns=['Python','Tensorflow','Keras'])

# 数据库连接
conn = create_engine('mysql+pymysql://root:123456@localhost/pandas?charset=UTF8MB4')
# 保存到数据库
df.to_sql('score',#数据库中表名
          conn,# 数据库连接
          if_exists='append')#如果表名存在,追加数据


# 从数据库中加载
pd.read_sql('select * from score limit 10', # sql查询语句
            conn, # 数据库连接
            index_col='Python') # 指定行索引名

第四节 HDF5

pip install tables -i https://pypi.tuna.tsinghua.edu.cn/simple

HDF5是一个独特的技术套件,可以管理非常大和复杂的数据收集。

HDF5,可以存储不同类型数据的文件格式,后缀通常是.h5,它的结构是层次性的。

一个HDF5文件可以被看作是一个组包含了各类不同的数据集

import numpy as np
import pandas as pd
df1 = pd.DataFrame(data = np.random.randint(0,50,size = [50,5]), # 薪资情况
               columns=['IT','化工','生物','教师','士兵'])
df2 = pd.DataFrame(data = np.random.randint(0,50,size = [150,3]),# 计算机科目的考试成绩
                   columns=['Python','Tensorflow','Keras'])

# 保存到当前路径下,文件命名是:data.h5
df1.to_hdf('data/data.h5',key='salary') # 保存数据的key,标记
df2.to_hdf('data/data.h5',key = 'score')

data = pd.read_hdf('data//data.h5',
            key = 'salary')#获取指定的标记、key的数据

display(data.head(5))

第五部分 数据选取

  • 字段数据
  • 标签选择
  • 位置选择
  • boolean索引
  • 赋值操作

第一节 字段数据

获取单列 - Series

import pandas as pd
import numpy as np
df = pd.DataFrame(data = np.random.randint(0,150,size = [150,3]),# 计算机科目的考试成绩
                   columns=['Python','Tensorflow','Keras'])
print('获取单列 - series:')
r1 = df['Python'] # 获取单列,Series
r2 = df.Python # 获取单列,Series
display(r1,r2)

获取多列/行切片 - DataFrame

import pandas as pd
import numpy as np
df = pd.DataFrame(data = np.random.randint(0,150,size = [150,3]),# 计算机科目的考试成绩
                   columns=['Python','Tensorflow','Keras'])

# 获取多列,DataFrame
r1 = df[['Python','Keras']] 
print('获取多列:')
display(r1)

# 行切片,DataFrame
r2 = df[3:5] 
print('行切片:')
display(r2)

# 根据单列转列表 - DataFrame
r3 = df[['Python']] 
print('单列转列表:')
display(r3)

第二节 标签选择 - loc

  • loc方法是用来对行或列的索引进行切片的
  • loc[index,columns]
  • loc[:,columns]
  • loc[index,:]
  • 具体索引名称 - 顾头又顾尾
  • 没有索引的顾头不顾尾

指定行/列标签

import pandas as pd
import numpy as np
df = pd.DataFrame(data = np.random.randint(0,150,size = [10,3]),# 计算机科目的考试成绩
                  index = list('ABCDEFGHIJ'),# 行标签
                  columns=['Python','Tensorflow','Keras'])
print('原数据:')
display(df)

# 选取指定行标签数据。
r1 = df.loc[['A','C','D','F']] 
print('选取指定行标签数据:')
display(r1)

# :默认保留所有行
r2 = df.loc[:,['Keras','Tensorflow']] 
print('选取指定列标签数据:')
display(r2)

指定范围 - 具体值/切片

# loc是对行索引 / 列索引的名称进行切片
# 所以是顾头又顾尾的切片
import pandas as pd
import numpy as np
df = pd.DataFrame(data = np.random.randint(0,150,size = [10,3]),# 计算机科目的考试成绩
                  index = list('ABCDEFGHIJ'),# 行标签
                  columns=['Python','Tensorflow','Keras'])
print('原数据:')
display(df)

# 根据行标签切片,选取指定列标签的数据
r1 = df.loc['A':'E',['Python','Keras']] 
print('选取指定行标签数据:')
display(r1)

# 选取标量值
r2 = df.loc['A','Python'] 
print('选取标量值:')
display(r2)

# 行切片从标签E开始每2个中取一个,列标签进行切片
r3 = df.loc['E'::2,'Python':'Tensorflow'] 
print('标签切片取:')
display(r3)


第三节 位置选择 - iloc

  • iloc 以0起始的自然数索引
  • iloc[:,:]
  • 自然索引 - 顾头不顾尾
import pandas as pd
import numpy as np
df = pd.DataFrame(data = np.random.randint(0,150,size = [10,3]),# 计算机科目的考试成绩
                  index = list('ABCDEFGHIJ'),# 行标签
                  columns=['Python','Tensorflow','Keras'])

# 用整数切片,类似NumPy
df.iloc[2:8,0:2] 
# 整数列表按位置切片
df.iloc[[1,3,5],[0,2,1]] 
# 行切片
df.iloc[1:3,:] 
 # 列切片
df.iloc[:,:2]
# 选取标量值
df.iloc[0,2] 

第四节 boolean索引

#  解释:1.  根据 列字段 筛选 返回的是True或False 的 series - 所以,对应的是 每个行 的真与假
#        2. 根据  行字段 筛选 返回的是True或False 的 series - 所以,对应的是 每个列 的真与假
#        3. 根据这个规则,我们要切记,行/列 不能混合使用
# 一般来说,对数据进行修改的时候,不应该直接修改原数据
# 故可以选用深拷贝的方式
# df_copy = df.copy() # 直接用这个
import pandas as pd
import numpy as np
df = pd.DataFrame(data = np.random.randint(0,150,size = [10,3]),# 计算机科目的考试成绩
                  index = list('ABCDEFGHIJ'),# 行标签,用户
                  columns=['Python','Tensorflow','Keras']) # 考试科目
#  判断Python分数是否大于100,返回值是boolean类型的Series
cond1 = df.Python > 100 
# 返回Python分数大于100分的用户所有考试科目数据
df[cond1] 

# &与运算 / |或运算 - 别忘记把条件加 () 识别
cond2 = (df.Python > 50) & (df['Keras'] > 50) 
# 返回Python和Keras同时大于50分的用户的所有考试科目数据
df[cond2] 

# 这个就是条件的延申 - 还是很重要的 - 具体可以看赋值操作
# 选择DataFrame中满足条件的值,如果满足返回值,不然返回空数据NaN
df[df > 50]

# isin判断是否在数组中,返回也是boolean类型值
df[df.index.isin(['A','C','F'])] 

# isnull 判断数据是否为空,并赋值
df.iloc[1,1] = np.NaN
df[df.isnull()] = 1024
df

第五节 赋值操作

  • 按字段赋值
  • 按标签赋值 - loc
  • 按位置赋值 - iloc
  • 按boolean赋值
import pandas as pd
import numpy as np
df = pd.DataFrame(data = np.random.randint(0,150,size = [10,3]),# 计算机科目的考试成绩
                  index = list('ABCDEFGHIJ'),# 行标签,用户
                  columns=['Python','Tensorflow','Keras']) # 考试科目
s = pd.Series(data = np.random.randint(0,150,size = 9),index=list('BCDEFGHIJ'),name = 'PyTorch')

# 增加一列,DataFrame行索引自动对齐
# 这里数据可以是series 也可以是列表
df['PyTorch'] = s

# 按标签赋值 - 标量赋值
df.loc['A','Python'] = 256 

# 按位置赋值
df.iloc[3,2] = 512 

# 按NumPy数组进行赋值
df.loc[:,'Python'] = np.array([128]*10) 

# 按照where条件进行赋值,大于等于128变成原来的负数,否则不变
df[df >= 128] = -df 
df

第六部分 数据集成

pandas 提供了多种将 Series、DataFrame 对象组合在一起的功能

  • concat
  • append(未来版本将取消)
  • merge
  • insert

第一节 concat/append数据串联 - 行/列

import pandas as pd
import numpy as np
df1 = pd.DataFrame(data = np.random.randint(0,150,size = [5,3]),# 计算机科目的考试成绩
                  index = list('ABCDE'),# 行标签,用户
                  columns=['Python','Tensorflow','Keras']) # 考试科目

df2 = pd.DataFrame(data = np.random.randint(0,150,size = [5,3]),# 计算机科目的考试成绩
                  index = list('FGHIJ'),# 行标签,用户
                  columns=['Python','Tensorflow','Keras']) # 考试科目

df3 = pd.DataFrame(data = np.random.randint(0,150,size = (5,2)),
                  index = list('ABCDE'),
                  columns=['PyTorch','Paddle'])

# 按照行拼接 - 最后一行数据末尾添加
r1 = pd.concat([df1,df2],axis = 0) # df1和df2行串联,df2的行追加df2行后面
print('按行拼接-contcat - axis = 0:')
display(r1)

r2 = df1.append(df2) # 在df1后面追加df2
print('按行拼接-append:')
display(r2)

# 按照列拼接 - 最后一列数据右边添加
# df3 = df3.append(df2)
# print('df3:')
# display(df3)
r3= pd.concat([df1,df3],axis = 1)  # df1和df2列串联,df2的列追加到df1列后面
print('按列拼接-contcat - axis = 1:')
display(r3)


# # 测试自然索引
# df_test1 = pd.DataFrame(np.random.randint(0,10,size=(3,3)))
# df_test2 = pd.DataFrame(np.random.randint(0,10,size=(5,2)))
# df_test = pd.concat([df_test1,df_test2],axis=1)
# df_test

第二节 insert - 插入

import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,151,size = (10,3)),
                  index = list('ABCDEFGHIJ'),
                  columns = ['Python','Keras','Tensorflow'])

# 按列插入 - 直接改变原数组 - 无返回值
index = list(df.columns).index('Python') + 1 # 后插  +1  前插 直接拿该名称的索引
# 方式一:指定值
df.insert(loc = index,column = 'Math',value = 150)
# 方式二:生成一个series
df.insert(loc = index,column = 'Chinese',value = np.random.randint(0,151,size = 10 ))

df

# # 补充说明
# # 对行的操作,使用追加append,默认在最后面,无法指定位置
# # 如果想要在指定位置插入行:切割-添加-合并
# df1 = pd.DataFrame(np.random.randint(0,10,size=(5,3)))
# # 把数据插入在第三行后面
# df2 = pd.DataFrame(np.zeros(shape=(2,3)))
# df_1_2 = pd.concat([df1.loc[:2,:],df2,df1.loc[3:,:]])
# df_1_2

第三节 merge - Join SQL风格合并

数据集的合并(merge)或连接(join)运算是通过一个或者多个键将数据链接起来的。
这些运算是关系型数据库的核心操作。pandas的merge函数是数据集进行join运算的主要切入点。

  • 这里研究merge的参数:
    • left : DataFrame
    • right : DataFrame or named Series
    • how : {'left', 'right', 'outer', 'inner', 'cross'}, default 'inner' # 合并方式
    • on : label or list # 共同的列属性
    • left_on : label or list, or array-like # 指定左边列属性(为基准)
    • right_on : label or list, or array-like # 指定右边列属性(为基准)
    • left_index : bool, default False # 没有共同列属性,以行索引为基准
    • right_index : bool, default False # 没有共同列属性,以行索引为基准
# 参数解析
'''
left : DataFrame
right : DataFrame or named Series
how : {'left', 'right', 'outer', 'inner', 'cross'}, default 'inner' # 合并方式
on : label or list # 共同的列属性
left_on : label or list, or array-like   # 指定左边列属性(为基准)
right_on : label or list, or array-like  # 指定右边列属性(为基准)
left_index : bool, default False         # 没有共同列属性,以行索引为基准
right_index : bool, default False        # 没有共同列属性,以行索引为基准
'''

指定左右列属性 left/right_on/on

'''
left : DataFrame
right : DataFrame or named Series
how : {'left', 'right', 'outer', 'inner', 'cross'}, default 'inner' # 合并方式
on : label or list # 共同的列属性
left_on : label or list, or array-like   # 指定左边列属性(为基准)
right_on : label or list, or array-like  # 指定右边列属性(为基准)
'''
import pandas as pd
import numpy as np
# 表一中记录的是name和体重信息
df1 = pd.DataFrame(data = {'name':['softpo','Daniel','Brandon','Ella'],'weight':[70,55,75,65]})
# 表二中记录的是name和身高信息
df2 = pd.DataFrame(data = {'name':['softpo','Daniel','Brandon','Cindy'],'height':[172,170,170,166]})
df3 = pd.DataFrame(data = {'名字':['softpo','Daniel','Brandon','Cindy'],'height':[172,170,170,166]})


# 利用共同列属性 -- 根据共同的name将俩表的数据,进行合并
r1 = pd.merge(df1,df2,
         how = 'inner',# 内合并代表两对象交集
         on = 'name')
display(r1)

# 利用列索引 -- 两边指定列属性名 - 作为合并的依据
r2 = pd.merge(df1,df3,
         how = 'outer',# 全外连接,两对象并集
         left_on = 'name',# 左边DataFrame使用列标签 name进行合并
         right_on = '名字')# 右边DataFrame使用列标签 名字进行合并
display(r2)

使用共同列索引 left/right_index

# 参数解析
'''
left : DataFrame
right : DataFrame or named Series
how : {'left', 'right', 'outer', 'inner', 'cross'}, default 'inner' # 合并方式
left_index : bool, default False         # 没有共同列属性,以行索引为基准
right_index : bool, default False        # 没有共同列属性,以行索引为基准
'''
import pandas as pd
import numpy as np
df4 = pd.DataFrame(data = np.random.randint(0,151,size = (10,3)),
                   index = list('ABCDEFHIJK'),
                   columns=['Python','Keras','Tensorflow'])
 # 计算每位学生各科平均分,转换成DataFrame
score_mean = pd.DataFrame(df4.mean(axis = 1).round(1),columns=['平均分'])

# 利用行索引 -- 将平均分和df3使用merge进行合并,它俩有共同的行索引
# 共同行索引
# df4 行索引 A ~ K
# df5 行索引 A~ K
pd.merge(left = df4,right = score_mean,
         left_index=True,# 左边DataFrame使用行索引进行合并
         right_index=True)# 右边的DataFrame使用行索引进行合并



第七部分 数据清洗

  • 重复数据 - drop_duplicates()
  • 空数据过滤 - isnull/dropna/fillna

重复数据 - drop_duplicates()

import numpy as np
import pandas as pd
df = pd.DataFrame(data = {'color':['red','blue','red','green','blue',None,'red'],
                          'price':[10,20,10,15,20,0,np.NaN]})
# 返回重复数据的boolen
df.duplicated()
# 删除重复数据 - 改变原数据
df.drop_duplicates()

空数据过滤 - isnull/dropna/fillna

# 计算时,None和NaN没有区别
# None Python的数据类型
# NaN是NumPy的数据类型
# 都表示空数据

import numpy as np
import pandas as pd
df = pd.DataFrame(data = {'color':['red','blue','red','green','blue',None,'red'],
                          'price':[10,20,10,15,20,0,np.NaN]})
# 判断是否存在空数据
# 方法一:可以查看每个数据的基本情况
df.info() # 总数为 7 
# 方法二:统计每列的空数据的个数
df.isnull().sum()

# 删除空数据
df.dropna()

# 填充空数据 -    def fillna(value,method,axis,inplace,limit)
# 均值填充
error_list = []
for column in list(df.columns[df.isnull().sum() > 0]):
    try:
        df[column].fillna(df[column].mean(),inplace=True)
    except Exception as e:
        error_list.append({column:e})
print(error_list)
        
            
# backfill/bfill - 后插  ffill/pad - 前插
df.fillna(method='ffill')
df.fillna(method='bfill')


指定行或者列过滤 - drop

import numpy as np
import pandas as pd
df = pd.DataFrame(data = {'color':['red','blue','red','green','blue',None,'red'],
                          'price':[10,20,10,15,20,0,np.NaN]})
del df['color'] # 直接删除某列
df.drop(labels = ['price'],axis = 1)# 删除指定列 - 原数据不变
df.drop(labels = [0,1,5],axis = 0) # 删除指定行 - 原数据不变
df.drop(labels = [0,1,2],axis = 0,inplace=True) # 删除指定行 - 改变原数据
df

函数filter使用 - 不常用(但还挺好用的)

import numpy as np
import pandas as pd

df = pd.DataFrame(np.array(([3,7,1], [2, 8, 256])),
                  index=['dog', 'cat'],
                  columns=['China', 'America', 'France'])
# 根据列索引名称
r1 = df.filter(items=['China', 'France'])
display(r1)

# 根据正则表达式删选列标签 - 对行/列索引名称检索
r2 = df.filter(regex='a$', axis=1)
display(r2)

# 选择行中包含og - 对行/列索引名称检索
r3 = df.filter(like='og', axis=0)
display(r3)

异常值过滤

$ > 3\sigma$ 异常值 \(\sigma\) 表示标准差

df2 = pd.DataFrame(data = np.random.randn(10000,3)) # 正态分布数据

# 3σ过滤异常值,σ即是标准差 : df2 大于三倍标准差的值
μ = df2.mean() # 均值
σ= df2.std()   # 标准差

# 方式一:获取每一列的异常
cond = df2.abs() > 3 * df2.std()
cond_0 = cond[0] # 默认[] 只能取列索引
cond_1 = cond[1]
cond_2 = cond[2]
# 逻辑或运算,只要有一个满足,返回True
cond_ = cond_0 | cond_1 | cond_2
display(df2[cond_])

# 方式二:过滤全部 - 
cond = df2.abs() > 3 * df2.std() # 计算异常值
# axis = 1计算每一行:只要一行中有一个TRUE,返回True
# True,表示异常值 - 只要符合异常点的数据,全部剔除
cond_ = cond.any(axis = 1) # 只要有一个为真,返回True
# cond_异常值
display(df2[cond_])
# 删除异常值
df2.drop(labels=df2[cond_].index,axis=0)

第八部分 数据转换

轴和元素替换 - rename/replace

import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,10,size = (10,3)),
                  index = list('ABCDEFHIJK'),
                  columns=['Python','Tensorflow','Keras'])
df.iloc[4,2] = None # 空数据
df

#1、重命名轴索引 -rename
df.rename(index = {'A':'AA','B':'BB'},columns = {'Python':'人工智能'}) 

# 2、替换值 -replace
# 方式一:根据数值 - 替换值
df.replace(5,1024) #将3替换为1024
df.replace([0,7],2048) # 将0和7替换为2048
# 方式二:根据字典 - 替换值
df.replace({0:512,np.nan:998},inplace=True) # 根据字典键值对进行替换
df.replace({'Python':1},-1024,inplace=True) # 将Python这一列中等于2的,替换为-1024
df

重设索引 reset_index

# 2、重设索引
import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,10,size = (10,3)),
                  index = list('ABCDEFHIJK'),
                  columns=['Python','Tensorflow','Keras'])
df.reset_index(drop=False,inplace=True)
df

map Series

  • map批量元素改变,Series专有
  • 字典
  • 函数映射
  • 隐式函数 - lambda
import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,10,size = (10,3)),
                  index = list('ABCDEFHIJK'),
                  columns=['Python','Tensorflow','Keras'])
df.iloc[4,2] = None # 空数据

# 方法一:字典映射 - map批量元素改变,Series专有
df['Keras'] = df['Keras'].map({9:'Hello',5:'World',np.NaN:'AI'}) # 字典映射
display(df)

# 方法二:函数映射
def convert(x):
    if x == 9:return 'Hello'
    elif x == 5:return 'World'
    elif x is np.NaN:return 'AI'
    else:return x
df['Keras'] = df['Keras'].map(convert)
display(df)

# 方式三:隐式函数 - lambda
df['Python'] = df['Python'].map(lambda x: x if x>6 else 1024 )
display(df)

apply元素改变。既支持 Series,也支持 DataFrame

apply - series - 同map用法相同

  • 函数
# apply - series 用法同map一样
import numpy as np

import pandas as pd
# 根据aplly 进行 单行或多行转换
df = pd.DataFrame(np.random.randint(0,100,size = (30,3)),columns=['Python','Math','En'])
# display(df)

def convert(x):
    if x < 60:
        return '不及格' # 条件满足,直接执行下一个
    elif x < 80: # 是否小于80,条件满足,60 <= x < 80
        return '中等'
    else:
        return '优秀'

for col in ['Python','Math','En']: # 已经字符换
    # 根据规则,进行数据转换
    result = df[col].apply(convert)

    # 插入位置的索引,使用变量表示
    index = list(df.columns).index(col) + 1

    # 插入
    df.insert(loc = index,column = col + '等级',value = result)

df

applymap - DataFrame专有

# 2、applymap DataFrame专有
df[['Python','Math']]= df[['Python','Math']].applymap(lambda x : 1024) # 计算DataFrame中每个元素
df

transform变形金刚

  • 可以同时转换多列
  • 可以同时对某列转换多个操作 - 列表
  • 可以同时转换多列 并做多个操作
  • 函数
  • 列表
  • 多列
import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,10,size = (10,3)),
                  index = list('ABCDEFHIJK'),
                  columns=['Python','Tensorflow','Keras'])
df.iloc[4,2] = None # 空数据
df
# 对一列,进行不同的操作 - transform - series
print('对一列进行不同的操作:')
r1 = df['Python'].transform(lambda x : x+100)
r2 = df['Python'].transform([np.sqrt,np.exp,convert])# 开平发,幂运算自然底数e
display(r1,r2)

#   对多列,进行不同的操作 - transform - DataFrame
print('对多列进行不同的操作:')
r1 = df[['Python','Tensorflow']].transform([np.sqrt,np.exp,convert])
r2 = df.transform({'Python':np.exp,'Tensorflow':lambda x:x+10,'Keras':np.sqrt})
r3 = df.apply({'Python':np.exp,'Tensorflow':lambda x:x+10,'Keras':np.sqrt})
display(r1,r2,r3)

重排随机抽样哑变量 - take/permutation

# take 是对DataFrame的位置顺序重新排列
import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,10,size = (10,3)),
                  index = list('ABCDEFHIJK'),
                  columns=['Python','Tensorflow','Keras'])
display(df)
# 重排  - take
index = np.random.permutation(10)
display(index)

# 随机抽样
index = np.random.randint(0,10,size = 5)
df.take(index)

# 哑变量,独热编码,1表示有,0表示没有 - 针对非数值型数据 即object
df = pd.DataFrame({'key':['b','b','a','c','a','b']}) # 不是数字了,字符串

df2 = pd.get_dummies(df,prefix='',prefix_sep='') # 哑变量,独热编码

df3 = pd.concat([df,df2],axis=0)
df3

第九部分 数据重塑

  • 转置
  • stack 堆叠
  • unstack 不堆叠
  • 多层索引

转置

import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,100,size = (10,3)),
                  index = list('ABCDEFHIJK'),
                  columns=['Python','Tensorflow','Keras'])
# 转置
r1 = df.T 
display(r1)

多层索引

多层索引之创建/取值

import numpy as np
import pandas as pd

# 将行索引设置为多层索引 - 要计算好生成的数据个数
df1 = pd.DataFrame(data=np.random.randint(0,100,size=(20,3)),
             index = pd.MultiIndex.from_product([list('ABCDEFHIJK'),['期中','期末']]),
             columns=['Python','Tensorflow','Keras'])
print('将行索引设置为多层索引:')
display(df1)

# 将行索引设置为多层索引 - 要计算好生成的数据个数
df2 = pd.DataFrame(data=np.random.randint(0,100,size=(10,6)),
             columns = pd.MultiIndex.from_product([['Python','Tensorflow','Keras'],['期中','期末']]),
             index=list('ABCDEFHIJK'))
print('将行索引设置为多层索引:')
display(df2)

# 多次索引取值
# df1 - 取出python的期末成绩
df1.loc['A','期中']['Python']

# df2- 取出python的期末成绩
df2['Python','期中']

多层索引之变形 - stack/unstack/level

  • 有关于level的介绍 : level 指的是堆叠测层级
  • 行索引- 从左到右 0,1,2,3...
  • 列索引- 从上到下 0,1,2,3...
# 有关于level的介绍 : level 指的是堆叠测层级
# 行索引- 从左到右  0,1,2,3...
# 列索引- 从上到下  0,1,2,3...
import numpy as np
import pandas as pd

# 将行索引设置为多层索引 - 要计算好生成的数据个数
df1 = pd.DataFrame(data=np.random.randint(0,100,size=(20,3)),
             index = pd.MultiIndex.from_product([list('ABCDEFHIJK'),['期中','期末']]),
             columns=['Python','Tensorflow','Keras'])
# print('将行索引设置为多层索引:')
# display(df1)


# 行索引右多个标签时 - 需要结束堆叠
# 行索引变成列索引,结构改变
# 默认情况下,最里层调整
# level = -1调整参数,可以改变行变列的顺序

df2 = df1.unstack()
print('取消 - 行堆叠:')
display(df2)



# 列索引右多个标签时 - 可以将其变成行索引堆叠
# stack堆,摞起来,一行行摞起来
# 作用:列索引变成行索引
df3 = df2.stack()
print('添加 - 行堆叠:')
display(df3)

# df1.to_csv('./test1.csv')
# df2.to_csv('./test2.csv')
# data = pd.read_csv('./test1.csv')
# data

多层索引之运算

import numpy as np
import pandas as pd

# 将行索引设置为多层索引 - 要计算好生成的数据个数
df1 = pd.DataFrame(data=np.random.randint(0,100,size=(20,3)),
             index = pd.MultiIndex.from_product([list('ABCDEFHIJK'),['期中','期末']]),
             columns=['Python','Tensorflow','Keras'])
# print('将行索引设置为多层索引:')
display(df1)

# 期中、期末消失 - 每个人,期中期末总分数
r1 = df1.sum(level = 0) # 算的是 行索引的最外层
r2 = df1.sum(level = 1) # 算的是 行索引的最里层
r3 = df1.sum(axis=0) # 按列取值
r4 = df1.sum(axis=0,level=0) # 按列取值 然后按照 行索引的最外层
r5 = df1.mean() # 求各学科平均值
display(r1,r2,r3,r4,r5)

第十部分 数学和统计方法

  • pandas对象拥有一组常用的数学和统计方法。它们属于汇总统计,
  • 对Series汇总计算获取mean、max值或者对DataFrame行、列汇总计算返回一个Series。

简单统计指标 - describe

  • axis = 0(列) / 1(行)
  • 以下指标 axis = 0(默认)
  • count # 统计非NA值的数量
  • max/min # 最大/小值
  • median # 中位数
  • sum/mean # 总和/平均值
  • quantile # 分位数
  • describe # 统计指标
import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,100,size = (20,3)),
                  index = list('ABCDEFHIJKLMNOPQRSTU'),
                  columns=['Python','Tensorflow','Keras'])
# 1、简单统计指标
df.count() # 非NA值的数量
df.max(axis = 0) #轴0最大值,即每一列最大值
df.min() #默认计算轴0最小值
df.median() # 中位数
df.sum() # 求和
df.mean(axis = 1) #轴1平均值,即每一行的平均值
df.quantile(q = [0.2,0.4,0.8]) # 分位数
df.describe() # 查看数值型列的汇总统计,计数、平均值、标准差、最小值、四分位数、最大值


# 补充:上述方法是pandas库自己提供的,也可以自己去写方法处理数据
# 可用 map / apply(map) / transform
df = pd.DataFrame(data = np.random.randint(0,100,size = (20,3)),
                  index = list('ABCDEFHIJKLMNOPQRSTU'),
                  columns=['Python','Tensorflow','Keras'])

def convert(x):
    if x > 80:
        return np.NaN
    else:
        return x
df['Python'] = df['Python'].map(convert)

df['Tensorflow'] = df['Tensorflow'].apply(convert)

df['Keras'] = df['Keras'].transform(convert)
df

索引标签arg/id- max/min、位置获取(最大值、最小值)

  • argmin - 返回最小值索引
  • argmax - 返回最大值索引
  • idxmax - 返回最大值索引 - axis = 0
  • idxmin - 返回最小值索引 - axis = 0
# 索引位置根据找到最大/小值 返回所用位置
import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,100,size = (20,3)),
                  index = list('ABCDEFHIJKLMNOPQRSTU'),
                  columns=['Python','Tensorflow','Keras'])

# 方法一:argmin/argmax
r1 = df['Tensorflow'].argmin() # 计算最小值位置,自然数0、1、2……
r2 = df['Keras'].argmax() # 最大值位置
display(r1,r2)

# 方法一:idxmax/idxmin - axis = 0(默认)
r3 = df.idxmax() # 最大值索引标签:index强标记
r4 = df.idxmin() # 最小值索引标签
display(r3,r4)

更多统计指标

  • value_counts # 统计元素出现次数
  • unique # 去重
  • cumsum # 累加
  • cumprod # 累乘
  • cummin # 累计最小值
  • cummax # 累计最大值
  • std # 标准差
  • var # 方差
  • diff # 计算差分
  • pct_change # 计算百分比变化
# 索引位置根据找到最大/小值 返回所用位置
import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,100,size = (20,3)),
                  index = list('ABCDEFHIJKLMNOPQRSTU'),
                  columns=['Python','Tensorflow','Keras'])

# 3、更多统计指标
df['Python'].value_counts() # 统计元素出现次数
df['Keras'].unique() # 去重
df.cumsum() # 累加
df.cumprod() # 累乘
df.std() # 标准差
df.var() # 方差
df.cummin() # 累计最小值
df.cummax() # 累计最大值
df.diff() # 计算差分
df.pct_change() # 计算百分比变化

高级统计指标

  • cov # 属性的协方差
  • corr # 所有属性相关性系数
  • corrwith # 单一属性相关性系数
# 索引位置根据找到最大/小值 返回所用位置
import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,100,size = (20,3)),
                  index = list('ABCDEFHIJKLMNOPQRSTU'),
                  columns=['Python','Tensorflow','Keras'])
# 4、高级统计指标
df.cov() # 属性的协方差
df['Python'].cov(df['Keras']) # Python和Keras的协方差
df.corr() # 所有属性相关性系数
df.corrwith(df['Tensorflow']) # 单一属性相关性系数

协方差:\(Cov(X,Y) = \frac{\sum\limits_1^n(X_i - \overline{X})(Y_i - \overline{Y})}{n-1}\)

相关性系数:\(r(X,Y) = \frac{Cov(X,Y)}{\sqrt{Var[X]Var[Y]}}\)

第十一部分 数据排序 - sort_/index/values - nlargest/nsmallest

import numpy as np
import pandas as pd
df = pd.DataFrame(data = np.random.randint(0,30,size = (30,3)),
                  index = list('qwertyuioijhgfcasdcvbnerfghjcf'),
                  columns = ['Python','Keras','Pytorch'])
display(df)


# 1、索引列名排序
df.sort_index(axis = 0,ascending=True) # 按索引排序,降序
df.sort_index(axis = 1,ascending=False) #按列名排序,升序
# 2、属性值排序
df.sort_values(by = ['Python']) #按Python属性值排序
df.sort_values(by = ['Python','Keras'],ignore_index=True,inplace=True)#先按Python,再按Keras排序

# 3、返回属性n大或者n小的值
df.nlargest(10,columns='Keras') # 根据属性Keras排序,返回最大10个数据
df.nsmallest(5,columns='Python') # 根据属性Python排序,返回最小5个数据

第十二部分 分箱操作

  • 分箱操作就是将连续数据转换为分类对应物的过程。比如将连续的身高数据划分为:矮中高。

  • 分箱操作分为等距分箱和等频分箱。

  • 分箱操作也叫面元划分或者离散化。

等宽分箱 - cut

  • 范围平均分配
# 等宽分箱必须是一维的 - cut
# 指定范围
import numpy as np
import pandas as pd
df1 = pd.DataFrame(data = np.random.randint(0,150,size = (100,3)),
                  columns=['Python','Tensorflow','Keras'])
display(df1)
# 方法一:等宽分项 - bins = 4
# 给学生成绩分为4档
df2 = pd.cut(df1.Python,bins=4,labels=['不及格','中等','良好','优秀'])
display(df2)

# 方法二:等宽分项 - 指定分箱宽度
df3 = pd.cut(df1['Tensorflow'],
             bins=[0,60,90,120,150],
             right=False, # 左闭右开
              labels=['不及格','中等','良好','优秀'])# 分箱后分类
display(df3)

# 补充
def convert(x):
    if x < 60:
        return '不及格'
    elif x < 90:
        return '中等'
    elif x < 120:
        return '良好'
    else:
        return '优秀'
df.Keras.map(convert)

等频分箱 - qcut

  • 数量平均分配
# 等频率分箱必须是一维的 - qcut
# 就是多个分类的数量是差不多的
import numpy as np
import pandas as pd
df1 = pd.DataFrame(data = np.random.randint(0,150,size = (100,3)),
                  columns=['Python','Tensorflow','Keras'])
display(df1)
df2 = pd.qcut(df.Python,q = 4,# 4等分
        labels=['差','中','良','优']).value_counts() # 分箱后分类
display(df2.value_counts)

第十三部分 分组聚合

分组 - groupby

# 分组
import numpy as np
import pandas as pd
# 准备数据
df = pd.DataFrame(data = {'sex':np.random.randint(0,2,size = 300), # 0男,1女
                          'class':np.random.randint(1,9,size = 300),#1~8八个班
                          'Python':np.random.randint(0,151,size = 300),#Python成绩
                          'Keras':np.random.randint(0,151,size =300),#Keras成绩
                          'Tensorflow':np.random.randint(0,151,size=300),
                          'Java':np.random.randint(0,151,size = 300),
                          'C++':np.random.randint(0,151,size = 300)})

df['sex'] = df['sex'].map({0:'男',1:'女'}) # 将0,1映射成男女

# 1、分组->可迭代对象
# 1.1 先分组再获取数据
g = df.groupby(by = 'sex',as_index=True)[['Python','Java']] # 单分组
for name,data in g:
    print('组名:',name)
    print('数据:',data)
# df.groupby(by = ['class','sex'])[['Python']] # 多分组


# 1.2 对一列值进行分组
df['Python'].groupby(df['class']) # 单分组
df['Keras'].groupby([df['class'],df['sex']]) # 多分组

# 1.3 按数据类型分组
df.groupby(df.dtypes,axis = 1)

# 1.4 通过字典进行分组
m = {'sex':'category','class':'category','Python':'IT','Keras':'IT',
     'Tensorflow':'IT','Java':'IT','C++':'IT'}
for name,data in df.groupby(m,axis = 1):
    print('组名',name)
    print('数据',data)

分组聚合 groupby + 统计指标

# 2、分组直接调用函数进行聚合
import numpy as np
import pandas as pd
# 准备数据
df = pd.DataFrame(data = {'sex':np.random.randint(0,2,size = 300), # 0男,1女
                          'class':np.random.randint(1,9,size = 300),#1~8八个班
                          'Python':np.random.randint(0,151,size = 300),#Python成绩
                          'Keras':np.random.randint(0,151,size =300),#Keras成绩
                          'Tensorflow':np.random.randint(0,151,size=300),
                          'Java':np.random.randint(0,151,size = 300),
                          'C++':np.random.randint(0,151,size = 300)})

# 按照性别分组,其他列均值聚合
r1 = df.groupby(by = 'sex').mean().round(1) # 保留1位小数
print('按照性别分组,其他列均值聚合:')
display(r1)

# 按照班级和性别进行分组,Python、Keras的最大值聚合
r2 = df.groupby(by = ['class','sex'])[['Python','Keras']].max().unstack()
print('按照班级和性别进行分组,Python、Keras的最大值聚合:')
display(r2)

# 按照班级和性别进行分组,计数聚合。统计每个班,男女人数
r3 = df.groupby(by = ['class','sex']).size()
print('按照班级和性别进行分组,计数聚合。统计每个班,男女人数:')
display(r3)

# 基本描述性统计聚合
r4 = df.groupby(by = ['class','sex']).describe()
print('基本描述性统计聚合:')
display(r4)

分组聚合apply、transform

# 3、分组后调用apply,transform封装单一函数计算
# 2、分组直接调用函数进行聚合
import numpy as np
import pandas as pd
# 准备数据
df = pd.DataFrame(data = {'sex':np.random.randint(0,2,size = 300), # 0男,1女
                          'class':np.random.randint(1,9,size = 300),#1~8八个班
                          'Python':np.random.randint(0,151,size = 300),#Python成绩
                          'Keras':np.random.randint(0,151,size =300),#Keras成绩
                          'Tensorflow':np.random.randint(0,151,size=300),
                          'Java':np.random.randint(0,151,size = 300),
                          'C++':np.random.randint(0,151,size = 300)})

# 返回分组结果
df.groupby(by = ['class','sex'])['Python'].apply(np.mean).round(1)
# 数据正则化
def normalization(x):
    return (x - x.min())/(x.max() - x.min()) # 最大值最小值归一化

# 返回全数据,返回DataFrame.shape和原DataFrame.shape一样。
df.groupby(by = ['class','sex'])[['Python','Tensorflow']].transform(normalization).round(3)

分组聚合agg

import numpy as np
import pandas as pd
# 准备数据
df = pd.DataFrame(data = {'sex':np.random.randint(0,2,size = 300), # 0男,1女
                          'class':np.random.randint(1,9,size = 300),#1~8八个班
                          'Python':np.random.randint(0,151,size = 300),#Python成绩
                          'Keras':np.random.randint(0,151,size =300),#Keras成绩
                          'Tensorflow':np.random.randint(0,151,size=300),
                          'Java':np.random.randint(0,151,size = 300),
                          'C++':np.random.randint(0,151,size = 300)})

# 4、agg 多中统计汇总操作
# 分组后调用agg应用多种统计汇总
df.groupby(by = ['class','sex'])[['Tensorflow','Keras']].agg([np.max,np.min,pd.Series.count])
# 分组后不同属性应用多种不同统计汇总
df.groupby(by = ['class','sex'])[['Python','Keras']].agg({'Python':[('最大值',np.max),('最小值',np.min)],
                                                          'Keras':[('计数',pd.Series.count),('中位数',np.median)]})

透视表pivot/_table - 重塑 + 聚合

  • pivot - 数据重塑
  • pivot_table - 数据重塑 + 聚合
  • pivot - 如果指定的 index + columns 构成的数据里面存在重复的情况,将会报错
  • pivot_table - index + columns 可同时设置
  • 参数介绍:
    • data:数据
    • index:字符串或列表 - 先分类 - 作为行索引(对某列进行分类结果,转换为行索引)
    • columns:字符串或列表 - 先分类 - 作为列索引(对某列进行分类结果,转换为列索引)
    • values:字符串或列表 - 指定透视分组的值
    • aggfunc:聚合函数 - 列表/字典 - 用法同数据转换类似
# 5、透视表
# pivot 与 pivot_table区别
# pivot 可以用来对数据的重塑
# pivot_table 对数据重塑的基础上做聚合
# 单从数据重塑上来说,效果是一样的
# 但一般来说,pivot_table区别用的更多一点
# 但是,如果指定的 index + columns 构成的数据里面存在重复的情况,将会报错


'''

pd.pivot_table(
    data: 'DataFrame',
    values=None,  # 数值型数据
    index=None,   # 分组透视的指标
    columns=None,
    aggfunc: 'AggFuncType' = 'mean', # 执行的运算

)
'''

# 透视表也是一种分组聚合运算
import numpy as np
import pandas as pd
# 准备数据
df = pd.DataFrame(data = {'sex':np.random.randint(0,2,size = 300), # 0男,1女
                          'class':np.random.randint(1,9,size = 300),#1~8八个班
                          'Python':np.random.randint(0,151,size = 300),#Python成绩
                          'Keras':np.random.randint(0,151,size =300),#Keras成绩
                          'Tensorflow':np.random.randint(0,151,size=300),
                          'Java':np.random.randint(0,151,size = 300),
                          'C++':np.random.randint(0,151,size = 300)})
# df['sex'].map({0:'男',1:'女'})
df['sex'] = df['sex'].transform(lambda x : '男' if x == 0 else '女')

# index columns 同时存在
# 将性别和班级作为行索引分类标准
df2 = pd.pivot_table( data=df,index=['sex','class'], values=['Python','Keras','Tensorflow'])

print('行索引分类标准:')
display(df2)

# 将将性别和班级作为列索引分类标准
df3 = pd.pivot_table(data=df,columns=['sex','class'],
               values=df.columns.drop(['sex','class']))
print('列索引分类标准:')
display(df3)

# 其实这里要如何编写自己的逻辑呢?
# 记住一点就是,x拿到的是series 
# 可以将其当作列表去处理
def count(x):
    return len(x)
# 这个是 班级和性别进行多层行索引的聚合
df4 = df.pivot_table(values=['Python','Keras','Tensorflow'],# 要透视分组的值
              index = ['class','sex'], # 分组透视指标
#               columns = ['class','sex'], # 分组透视指标
              aggfunc={'Python':[('计数',count)], # 聚合运算 
                       'Keras':[('最大值',np.max),('最小值',np.min)],
                       'Tensorflow':[('求和',lambda x:np.sum(x)),('平均值',np.mean),('中位数',np.median)]
                       } )
print('多聚合函数:')
display(df4)


# def count(x):
#     return len(x)
# df.pivot_table(values=['Python','Keras','Tensorflow'],# 要透视分组的值
#                index=['class','sex'], # 分组透视指标
#                aggfunc={'Python':[('最大值',np.max)], # 聚合运算
#                         'Keras':[('最小值',np.min),('中位数',np.median)],
#                         'Tensorflow':[('最小值',np.min),('平均值',np.mean),('计数',count)]})

第十四部分 时间序列

时间戳操作 -创建/转换

时间戳操作 - 创建 - period_range

  • Timestamp - 时刻 - 时间戳
  • Period - 时期
  • date_range - 批量时刻 - 时间戳
  • period_range - 批量时期
  • 参数:
    • start
    • end
    • periods
    • freq
      • Y/M/D H:T:S
import pandas as pd
import numpy as np
# 1、创建方法
# (1)时刻时间
t1 = pd.Timestamp('2022-2-22 ')
display(t1)
# (2)时期时间
t2 = pd.Period('2022-2-22',freq='M')
display(t2)


# 批量时刻/时期数据(常用) 
# start(开始) end(结束) periods(步长) freq(规则) name(名称)
# start end periods 只需要两个
# Y(year)  M(month) D(day) H(hour) T(minute) S(second) W(weekday)

# (3)批量时刻 - date_range
import datetime
t3 = pd.date_range(start = datetime.datetime.now(),periods=5,freq='D',name='时间')
display(t3)
# (4)批量时期 -  period_range
t4 = pd.period_range(start = datetime.datetime.now(),periods=5,freq='D',name='时间')
display(t4)
# (5)案例 - 给数据构建时刻索引 - 这个也很关键,可以为数据,插入时间序列
ts = pd.Series(np.random.randint(0,10,size=(5)),index=t3)
display(ts)

时间戳操作 - 转换 - do_datetime

  • to_datetime - 转换为标准时间格式
    • 参数:unit 转换标准
  • DateOffset - 时间补偿
    • 补偿量 - 可正可负
    • years
    • months
    • weeks
    • days
    • hours
    • minutes
    • seconds
    • milliseconds
    • microseconds
    • nanoseconds
import pandas as pd
import numpy as np
# 2、转换方法
t1 = pd.to_datetime(['2020.08.24','2020-08-24','24/08/2020','2020/8/24'])
t2 = pd.to_datetime([1598582232],unit='s')

# 时间计算
dt = pd.to_datetime([1598582420401],unit = 'ms') # 世界标准时间
print('世界标准时间:')
display(dt)
dt2 = dt + pd.DateOffset(hours = 8) # 东八区时间
print('东八区时间:')
display(dt2)
dt3 = dt + pd.DateOffset(days = 100) # 100天后日期
print('东八区时间100天:')
display(dt3)

时间戳索引

  • str类型索引
  • 时间戳索引
  • 时间戳索引属性
import pandas as pd
import numpy as np

index = pd.date_range("2020-8-24", periods=200, freq="D")
ts = pd.Series(range(len(index)), index=index)
# str类型索引
ts['2020-08-30'] # 日期访问数据
ts['2020-08-24':'2020-09-1'] # 日期切片
ts['2020-08'] # 传入年月
ts['2020'] # 传入年

# 时间戳索引
ts[pd.Timestamp('2020-08-30')]
ts[pd.Timestamp('2020-08-24'):pd.Timestamp('2020-08-30')] # 切片
ts[pd.date_range('2020-08-24',periods=10,freq='D')]

# 时间戳索引属性
ts.index.year # 获取年
ts.index.dayofweek # 获取星期几
ts.index.weekofyear # 一年中第几个星期几

时间序列常用方法 - shift/asfreq/resample

在做时间序列相关的工作时,经常要对时间做一些移动/滞后、频率转换、采样等相关操作,我们来看下这些操作如何使用

  • shift
    • periods - 数据移动
    • freq - 日期移动
  • asfreq - 频率转换
  • resample -
    • 日期维度 - 重采样
    • DataFrame - 重采样
import pandas as pd
import numpy as np

index = pd.date_range('8/1/2020', periods=365, freq='D')
ts = pd.Series(np.random.randint(0, 500, len(index)), index=index)
ts
# 1.1、移动 - shift - 可用作于数据模拟
ts.shift(periods = 2) #  数据后移 
ts.shift(periods = -2) # 数据前移

# 1.2、日期移动 - 加上freq后 后移几位,前面数据就失去几位
ts.shift(periods = 2,freq = pd.tseries.offsets.Day()) # 天移动
ts.tshift(periods = 1,freq = pd.tseries.offsets.MonthBegin()) #月移动

# 2、频率转换
ts.asfreq(pd.tseries.offsets.Week()) # 天变周
ts.asfreq(pd.tseries.offsets.MonthEnd()) # 天变月
ts.asfreq(pd.tseries.offsets.Hour(),fill_value = 0) #天变小时,又少变多,fill_value为填充值

# 3、重采样
# resample 表示根据日期维度进行数据聚合,可以按照分钟、小时、工作日、周、月、年等来作为日期维度
ts.resample('2W').sum() # 以2周为单位进行汇总
ts.resample('3M').sum().cumsum() # 以季度为单位进行汇总

# 4、DataFrame重采样
d = dict({'price': [10, 11, 9, 13, 14, 18, 17, 19],
          'volume': [50, 60, 40, 100, 50, 100, 40, 50],
          'week_starting':pd.date_range('24/08/2020',periods=8,freq='W')})
df1 = pd.DataFrame(d)

df1.resample('M',on = 'week_starting').apply(np.sum)
df1.resample('M',on = 'week_starting').agg({'price':np.mean,'volume':np.sum})


days = pd.date_range('1/8/2020', periods=4, freq='D')
data2 = dict({'price': [10, 11, 9, 13, 14, 18, 17, 19],
           'volume': [50, 60, 40, 100, 50, 100, 40, 50]})
df2 = pd.DataFrame(data2,
                   index=pd.MultiIndex.from_product([['morning','afternoon'],days]))
display(df2)
df2.resample('D', level=1).sum()

时区表示

index = pd.date_range('8/1/2012 00:00', periods=5, freq='D')
ts = pd.Series(np.random.randn(len(index)), index)
import pytz
pytz.common_timezones # 常用时区
# 时区表示
ts = ts.tz_localize(tz='UTC')
# 转换成其它时区
ts.tz_convert(tz = 'Asia/Shanghai')

第十五部分 数据可视化

pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple

线性图(plot) - 趋势变化

import numpy as np
import pandas as pd

# 1、线形图 - 趋势变化
df1 = pd.DataFrame(data = np.random.randn(1000,4),
                  index = pd.date_range(start = pd.to_datetime('27/6/2012'),periods=1000),
                  columns=list('ABCD'))
df1.cumsum().plot()

条形图(bar) - 对比

import numpy as np
import pandas as pd
# 2、条形图
df2 = pd.DataFrame(data = np.random.rand(10,4),
                   columns = list('ABCD'))
df2.plot.bar(stacked = True,color = np.random.random(size=(3,4)) ) # stacked 是否堆叠

饼图(pie) - 占比/分布

import numpy as np
import pandas as pd
# 3、饼图
df3 = pd.DataFrame(data = np.random.rand(4,2),
                   index = list('ABCD'),
                   columns=['One','Two'])
df3.plot.pie(subplots = True,figsize = (8,8))

散点图(scatter) - 相关性

import numpy as np
import pandas as pd

# 4、散点图
df4 = pd.DataFrame(np.random.rand(50, 4), columns=list('ABCD'))
df4.plot.scatter(x='A', y='B') # A和B关系绘制

# 在一张图中绘制AC散点图,同时绘制BD散点图
ax = df4.plot.scatter(x='A', y='C', color='DarkBlue', label='Group 1');
df4.plot.scatter(x='B', y='D', color='DarkGreen', label='Group 2', ax=ax)

# 气泡图,散点有大小之分
df4.plot.scatter(x='A',y='B',s = df4['C']*200)

# 做个相关性的数据
df =  pd.DataFrame(np.random.rand(50, 4), columns=list('ABCD'))
df['E'] = df['A'].transform(lambda x : x * 3 + 3)
df.plot.scatter(x='A',y='E')

直方图

import numpy as np
import pandas as pd

df7 = pd.DataFrame({'A': np.random.randn(1000) + 1, 'B': np.random.randn(1000),
                    'C': np.random.randn(1000) - 1})
df7.plot.hist(alpha=0.5) #带透明度直方图
df7.plot.hist(stacked = True)# 堆叠图
df7.hist(figsize = (8,8)) # 子视图绘制

面积图

import numpy as np
import pandas as pd

df5 = pd.DataFrame(data = np.random.rand(10, 4), 
                   columns=list('ABCD'))
df5.plot.area(stacked = True);# stacked 是否堆叠

箱式图

import numpy as np
import pandas as pd
df6 = pd.DataFrame(data = np.random.rand(10, 5), 
                   columns=list('ABCDE'))
df6.plot.box()

第十六部分 实战-数据分析师招聘数据分析

第一节 分析目标

  • 各城市对数据分析岗位的需求情况
  • 不同细分领域对数据分析岗的需求情况
  • 数据分析岗位的薪资状况
  • 工作经验与薪水的关系
  • 公司都要求什么掌握什么技能
  • 岗位的学历要求高吗
  • 不同规模的企业对工资经验的要求以及提供的薪资水平

第二节 数据加载

# import pandas as pd
# import numpy as np
# job = pd.read_csv('./job.csv')
# job.drop_duplicates(inplace = True) # 删除重复数据

第三节 数据清洗

过滤非数据分析的岗位

# # 数据分析相应的岗位数量
# cond = job["positionName"].str.contains("数据分析")  # 职位名中含有数据分析字眼的
# # 筛选出我们想要的字段,并剔除positionName
# job = job[cond]
# job.reset_index(inplace=True) # 行索引 重置
# job

数据中的薪水是一个区间,这里用薪水区间的均值作为相应职位的薪水

# 处理过程
#1、将salary中的字符串均小写化(因为存在8k-16k和8K-16K)
#2、运用正则表达式提取出薪资区间
#3、将提取出来的数字转化为int型
#4、取区间的平均值
# job["salary"] = job["salary"].str.lower()\
#                .str.extract(r'(\d+)[k]-(\d+)k')\
#                .applymap(lambda x:int(x))\
#                .mean(axis=1)

从job_detail中提取出技能要求 将技能分为以下几类

Python
SQL
Tableau
Excel
SPSS/SAS
处理方式: 如果job_detail中含有上述五类,则赋值为1,不含有则为0

# job["job_detail"] = job["job_detail"].str.lower().fillna("")  #将字符串小写化,并将缺失值赋值为空字符串
# job["Python"] = job["job_detail"].map(lambda x:1 if ('python' in x) else 0)
# job["SQL"] = job["job_detail"].map(lambda x:1 if ('sql' in x) or ('hive' in x)  else 0)
# job["Tableau"] = job["job_detail"].map(lambda x:1 if 'tableau' in x  else 0)
# job["Excel"] = job["job_detail"].map(lambda x:1 if 'excel' in x  else 0)
# job['SPSS/SAS'] = job['job_detail'].map(lambda x:1 if ('spss' in x) or ('sas' in x) else 0)

处理行业信息

在行业信息中有多个标签,对其进行处理,筛选最显著的行业标签。

# def clean_industry(industry):
#     industry = industry.split(",")
#     if industry[0]=="移动互联网" and len(industry)>1:
#         return industry[1]
#     else:
#         return industry[0]
# job["industryField"] = job.industryField.map(clean_industry)