pandas高级操作

pandas数据分析基础操作

numpy已经可以帮助我们进行数据的处理了,那么学习pandas的目的是什么呢?

numpy能够帮助我们处理的是数值型的数据,当然在数据分析中除了数值型的数据还有好多其他类型的数据比如,文本数据或时间序列等,那么pandas就可以帮我们很好的处理除了数值型的其他类型数据!

什么是pandas?

Pandas 库是一个免费、开源的第三方 Python 库,是 Python 数据分析和机器学习必不可少的工具之一,它为 Python 数据分析提供了高性能,且易于使用的数据结构。Pandas 自诞生后被应用于众多的领域,比如金融、统计学、社会科学、建筑工程等。

Pandas 库基于 Python NumPy 库开发而来,因此,它可以与 Python 的科学计算库配合使用。Pandas 提供了两种数据结构,分别是 Series(一维数组结构)与 DataFrame(二维数组结构),这两种数据结构极大地增强的了 Pandas 的数据分析能力。

Series

概述

Series是一种类似与一维数组的对象,由下面两个部分组成:

  • values:一组数据
  • index:相关的数据索引标签

常见操作

  • 创建方式
    • 由列表或numpy数组创建
    • 由字典创建
import pandas as pd
import numpy as np
from pandas import Series

s1 = Series(data=[1,2,3])
s2 = Series(data=np.random.randint(0,100,size=(3,)))
  • Series的索引
    • 隐式索引:默认形式的索引(0,1,2....)
    • 显示索引:自定义的索引,可以通过index参数设置显示索引
      • 显示索引的作用:增加了数据的可读性
s3 = Series(data=[1,2,3],index=['A','B','C'])
  • Series的索引和切片
s4 = Series(data=[1,2,3],index=['A','B','C'])
s4[0],4s['A'],s4.A #索引操作
s4[0:2],s4['A':'B']#切片操作
  • 可以使用布尔值充当Series的索引来使用

    se3 = Series(data=[1,2,3,4])
    se3[[True,False,True,False]]
    #返回结果:[1,3],可以将True对应的元素返回,False对应的元素忽略
    
  • Series的常用属性

    • shape
    • size
    • index
    • values
  • Series的常用方法

    • head(),tail()
    • unique() 元素去重,nunique() 统计去重后元素的个数
    • value_counts() 统计每一个元素出现的次数
    • isnull(),notnull()
    • add() sub() mul() div()
  • Series的运算法则

se1 = Series(data=[1,2,3],index=['a','b','c'])
se2 = Series(data=[1,2,3],index=['a','d','c'])

#返回结果为:
a    2.0
b    NaN
c    6.0
d    NaN
dtype: float64

运算法则:两个Series进行算数运算时,只有索引一致的元素可以进行算数运算,否则补空。

DataFrame

概述

  • DataFrame是一个【表格型】的数据结构。DataFrame由按一定顺序排列的多列数据组成。设计初衷是将Series的使用场景从一维拓展到多维。DataFrame既有行索引,也有列索引。
    • 行索引:index
    • 列索引:columns
    • 值:values

DataFrame的创建

  • 列表创建
  • 数组创建
  • 字典创建
import pandas as pd
import numpy as np
from pandas import Series,DataFrame

df1 = DataFrame(data=[[1,2],[3,4]],index=('A','B'),columns=['a','b'])
df2 = DataFrame(data=np.random.randint(0,100,size=(7,8)))
dic = {
    '张三':[150,150,150,300],
    '李四':[0,0,0,0]
}
df3 = DataFrame(data=dic,index=['语文','数学','英语','理综'])

DataFrame的常用属性

  • values,columns,index,shape

索引操作

  • 对行进行索引
  • 对列进行索引
  • 对元素进行索引
df = DataFrame(data=np.random.randint(0,100,size=(5,4)),index=['a','b','c','d','e'],columns=['A','B','C','D'])

#索引取单列
df['A'] #索引取单列(如果df表格存在显示的列索引,此时无法使用隐式列索引进行取值)
#索引取多列
df[['A','C']]
#在存在显示列索引的情况下,使用隐式索引取列
df.iloc[:,0] #逗号左侧是行,右侧是列

#索引取单行
df.iloc[0] #iloc后面使用隐式索引
df.loc['a'] #loc后面使用显示索引
#索引取多行
df.iloc[[0,2]]
df.loc[['a','c']]

#索引取单个元素
df.iloc[0,2]
df.loc['a','C']
#索引取多个元素
df.iloc[1,[0,1,2]]
df.loc['b',['A','B','C']]

切片操作

  • 批量切行
  • 批量切列
df[0:2]  #切行 df[index1:index3]
df.iloc[:,0:2] #切列
df.loc[:,'A':'C'] #切列

数据查看

查看DataFrame的概览和统计信息

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.head(10) # 显示头部10行,默认5个
df.tail(10) # 显示末尾10行,默认5个
df.describe() # 查看数值型列的汇总统计,计数、平均值、标准差、最小值、四分位数、最大值
	#如果数据表格中全部为非数值型的列,返回的统计指标:count非空元素个数,unique元素去重后的个数,top出现频率最高的元素,freq最大频率
df.info() # 查看列索引、数据类型、非空计数和内存信息

数据保存与加载

csv
import numpy as np
import pandas as pd
df = DataFrame(data = np.random.randint(0,50,size = [50,5]), # 薪资情况
               columns=['IT','化工','生物','教师','士兵'])
# 保存到当前路径下,文件命名是:salary.csv。csv逗号分割值文件格式
df.to_csv('./salary.csv',
          sep = ';', # 文本分隔符,默认是逗号
          header = True,# 是否保存列索引
          index = True) # 是否保存行索引,保存行索引,文件被加载时,默认行索引会作为一列
# 加载
pd.read_csv('./salary.csv',
            sep = ';',# 默认是逗号
            header = [0],#指定列索引
            index_col=0) # 指定行索引
pd.read_table('./salary.csv', # 和read_csv类似,读取限定分隔符的文本文件
            sep = ';',
            header = [0],#指定列索引
            index_col=1) # 指定行索引,IT作为行索引

Excel

环境安装:

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

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

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

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'])
# 保存到当前路径下,文件命名是:salary.xls
df1.to_excel('./salary.xls',
            sheet_name = 'salary',# Excel中工作表的名字
            header = True,# 是否保存列索引
            index = False) # 是否保存行索引,保存行索引
pd.read_excel('./salary.xls',
              sheet_name=0,# 读取哪一个Excel中工作表,默认第一个
              header = 0,# 使用第一行数据作为列索引
              names = list('ABCD'),# 替换行索引
              index_col=1)# 指定行索引,B作为行索引
sql

环境安装:

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

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

#读取数据库中的数据到DataFarame中
import pymysql
#创建数据库连接对象
conn = pymysql.Connect(
        host = '127.0.0.1', #数据库服务器的ip
        port = 3306, #端口号
        user = 'root', #用户名
        password = 'boboadmin', #密码
        db = 'testdb' #数据仓库名字
    )

LC = pd.read_sql(sql='select * from LC',con=conn)
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'])
# 数据库连接
#mysql+pymysql://用户名:密码@IP:Port/dbName?charset=UTF8MB4
conn = create_engine('mysql+pymysql://root:boboadmin@127.0.0.1:3306/spider?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(Hierarchical Data Format 5)是一种用于存储和管理大型科学数据集的文件格式和数据模型。pandas库提供了对HDF5文件的支持,可以方便地读取、写入和管理HDF5文件中的数据。

HDF5文件是一种包含多个数据对象的层次结构文件,可以存储多维数组、表格、图像和其他类型的数据。HDF5使用一种灵活的数据模型,可以有效地组织和处理庞大的数据集。

pandas中的HDF5功能允许用户将DataFrame对象或其他数据类型保存为HDF5文件,以便后续的快速读取和分析。HDF5文件的主要作用和场景如下:

  1. 存储大型数据集:HDF5可以处理大规模的数据集,适用于存储和管理需要占用大量内存的数据。通过使用HDF5,可以将数据保存在磁盘上,释放内存并提高系统的效率。
  2. 快速读取和索引:HDF5文件使用一种索引结构来快速定位数据,可以快速读取和查询指定的数据对象。这对于需要频繁地访问大数据集的应用非常有用。
  3. 数据共享和交流:HDF5文件可以在不同的平台和编程语言之间进行共享和交流。不同的团队可以将数据保存为HDF5文件,并在不同的分析环境中使用pandas来读取和处理数据。

总之,pandas的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.h5',key='salary') # 保存数据的key,标记
df2.to_hdf('./data.h5',key = 'score')

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

股票分析案例

首先,安装tushare财经数据接口包:pip install tushare

  • 使用tushare的get_k_data()方法获取指定股票的历史交易数据,并保存为DataFrame对象:
import tushare as ts
import pandas as pd

# 设置ts.get_k_data()的参数
stock_code = '000001'  # 股票代码:平安银行
start_date = '2021-01-01'  # 开始日期
end_date = '2023-2-12'  # 结束日期

# 获取股票历史交易数据
df = ts.get_k_data(code=stock_code, start=start_date, end=end_date)  

#将df中的数据存储到本地
df.to_csv('./gupiao.csv')

#可以将本地的文件数据读取到df
df = pd.read_csv('./gupiao.csv')

#删除指定的列
df.drop(labels='Unnamed: 0',axis=1,inplace=True) #axis=0表示的行1表示的是列

# 将日期列转换为日期类型
df['date'] = pd.to_datetime(df['date'])  

#将date作为原数据的行索引
df.set_index('date',inplace=True)
  • 计算股票的每日收益率和7日波动率:通过计算收益率和波动率,我们可以评估股票的风险和收益情况。
    • 每日收益率:(当日收盘价 - 前一日的收盘价)/ 前一日的收盘价
    • 7日波动率:对每日收益率数据进行每7日滚动的方差计算
# 计算收益率和波动率
return_rate = df['close'].pct_change() * 100  # 收益率
#或者:(df['close'] - df['close'].shift(1)) / df['close'].shift(1) * 100

volatility_rate = return_rate.rolling(window=7).var()  # 波动率
  • 查找股票的市值最大和最小日
# 市值 = 收盘价 * 成交量
market_value = df['close'] * df['volume']  
#找出市值数据中最大最小值下标(市值最大和最小日期)
max_market_value_day = market_value.idxmax()
min_market_value_day = market_value.idxmin()

print(max_market_value_day)
print(min_market_value_day)

  • 输出该股票所有收盘比开盘上涨3%以上的日期
#方式1:
ex = (df['close'] - df['open']) / df['open'] > 0.03
ex[ex].index
#方式2:
#(收盘-开盘) / 开盘 > 0.03
((df['close'] - df['open']) / df['open'])  > 0.03

#获取了True对应的行数据:就是满足需求的行数据
df.loc[((df['close'] - df['open']) / df['open'])  > 0.03]

#基于index获取满足需求行数据的行索引
df.loc[((df['close'] - df['open']) / df['open'])  > 0.03].index
  • 输出该股票所有开盘比前日收盘跌幅超过2%的日期
#(开盘-前日收盘)/前日收盘 < -0.02
((df['open']-df['close'].shift(1))/df['close'].shift(1)) < -0.02

#定位满足要求的日期
df.loc[((df['open']-df['close'].shift(1))/df['close'].shift(1)) < -0.02].index
  • 假如张三从2023年5月1日开始,每月第一个交易日买入1手股票,每年最后一个交易日卖出所有股票,到今天为止,我的收益如何?

    • 分析:
      • 买入股票
        • 一个完整的年,需要买入12手1200支股票。以购买当期的开盘价进行股票的买卖。
      • 卖出股票
        • 一个完整的年,需要卖出1200支股票(开盘价为单价)
      • 特殊情况:
        • 最后一年就是一个特殊的年(因为没有到该年最后一个交易日),只可以买不可以卖,但是手里剩余的股票是需要计算到总收益中。
    • resample函数介绍:pandas库中的resample函数主要用于将时间序列数据重新采样到不同的时间频率,例如从按天采样重新采样为按周或按月采样。resample函数的常用语法如下:
    df.resample(rule, ...).func()
    其中,df是一个时间序列数据的DataFrame,rule是指定重采样频率的规则字符串(H小时、W星期、M月、A年等),func是用于聚合数据的函数(例如求和、平均值等)。例如:
    df.resample('H').mean()
    df.resample('W').sum()
    df.resample('M').max()
    
    • 实现代码:
    #将2022-2024区间的数据取出
    new_df = df['2022':'2024']
    
    #对数据进行指定的取样
    df_monthly = new_df.resample('M').first()
    #购买股票一共花费的金额
    cost = df_monthly['open'].sum()*100
    #对数据进行指定的取样
    df_yearly = new_df.resample('A').last()[:-1]
    #卖出股票
    recv = df_yearly['open'].sum()*1200
    #计算剩余股票的价值
    last_price = 最后已过的月份数量*100*new_df['close'][-1]
    #总收益
    last_price+recv-cost
    
  • 计算该股票历史数据的5日均线和30日均线

    • 什么是均线?
      • 对于每一个交易日,都可以计算出前N天的移动平均值,然后把这些移动平均值连起来,成为一条线,就叫做N日移动平均线。移动平均线常用线有5天、10天、30天、60天、120天和240天的指标。
        • 5天和10天的是短线操作的参照指标,称做日均线指标;
        • 30天和60天的是中期均线指标,称做季均线指标;
        • 120天和240天的是长期均线指标,称做年均线指标。
    • 均线计算方法:MA=(C1+C2+C3+...+Cn)/N C:某日收盘价 N:移动平均周期(天数)
    ma5 = df['close'].rolling(5).mean()
    ma30 = df['close'].rolling(30).mean()
    
    #将均线两列添加到原始数据中
    df['ma5'] = ma5
    df['ma30'] = ma30
    
    
    import matplotlib.pyplot as plt
    plt.plot(md5,label='ma5')
    plt.plot(md30,label='ma30')
    plt.legend()
    

pandas数据分析数据清洗

数据清洗

概述

数据清洗是指对原始数据进行处理和转换,以去除无效、重复、缺失或错误的数据,使数据符合分析和建模的要求。

作用和意义

  • 提高数据质量:
    • 通过数据清洗,数据质量得到提升,减少错误分析和错误决策。
  • 增加数据可用性:
    • 清洗后的数据更加规整和易于使用,提高数据的可用性和可读性。
  • 支持分析和建模:
    • 清洗数据是进行数据分析和建模的前置步骤,只有高质量的数据才能得到准确和可靠的分析结果。
  • 保护隐私和数据安全:
    • 在数据清洗过程中可以对敏感数据进行匿名化或删除,保护隐私和数据安全。

清洗维度

  • 缺失值处理:
    • 对于缺失的数据,可以删除包含缺失值的行或列或者填充缺失值。
  • 重复值处理:
    • 识别和删除重复的数据行,避免重复数据对分析结果产生误导。
  • 异常值处理:
    • 检测和处理异常值,决定是删除、替换或保留异常值。

缺失值清洗

缺失值/空值的删除
  • 伪造缺失值数据
import pandas as pd
from pandas import DataFrame,Series
import numpy as np

df = DataFrame(data=np.random.randint(0,100,size=(7,5)))
df.iloc[0,3] = np.nan
df.iloc[3,3] = None
df.iloc[2,2] = np.nan
df.iloc[5,3] = np.nan
df
  • 缺失值的检测和删除
相关方法:
- isnull():检测df中的每一个元素是否为空值,为空则给该元素返回True,否则返回False
- notnull():检测df中的每一个元素是否为非空值,为非空则给该元素返回True,否则返回False
- any():检测一行或一列布尔值中是否存在一个或多个True,有则返回True,否则返回False
- all():检测一行或一列布尔值中是否存全部为True,有则返回True,否则返回False
- dropna():将存在缺失值/空值的行或者列进行删除
  • isnull()结合any()进行空值检测和过滤
ex = df.isnull().any(axis=1)#检测空值对应的行,轴向0表示行,1表示列
drop_index = df.loc[ex].index#获取空值对应的行索引
df.drop(index=drop_index)#删除空值对应的行
  • notnull()结合all()进行空值检测和过滤
ex = df.notsnull().all(axis=1)#检测空值对应的行,轴向0表示行,1表示列
df.loc[ex]#过滤空值对应的行
  • dropna()进行空值检测和过滤
df.dropna(axis=0) #注意在dropna中的轴向0表示行,1表示列
  • 计算df中每一列存在缺失值的个数和占比
for col in df.columns:
    #col表示df的某一列的列索引
    #判断列中是否存在空值
    if df[col].isnull().sum() > 0:
        #计算空值的占比
        p = df[col].isnull().sum() / df[col].size
        #将p小数转换成百分比显示
        p = format(p,'.2%') #.2%表示将p转换成保留2位小数的百分数
        print(col,'列中存在空值的占比为:',p)
缺失值/空值的填充
相关方法:
	- fillna(value,method,axis)
参数介绍:
	- value:给空值填充的值
	- method:填充方式,可以为bfill向后填充和ffill向前填充
	- axis:填充轴向
  • 使用任意值填充空值
df.fillna(value=666) #将df中所有的空值填充为666
  • 使用近邻值填充空值
df.fillna(method='bfill',axis=0) #在竖直方向上选择空后面的元素填充空值
#method填充方式:ffill(向前填充)和bfill(向后填充)
  • 使用相关的统计值填充空值
#使用列的均值填充列的空值
for col in df.columns:
    if df[col].isnull().sum() > 0:
        mean_value = df[col].mean()
        df[col].fillna(value=mean_value,inplace=True)

注意:实现空值的清洗最好选择删除的方式,如果删除的成本比较高,再选择填充的方式。

重复值清洗

  • 伪造重复行的数据源
df = DataFrame(data=np.random.randint(0,100,size=(8,5)))
df.iloc[3] = [0,0,0,0,0]
df.iloc[5] = [0,0,0,0,0]
df.iloc[7] = [0,0,0,0,0]
  • 使用duplicated()方法检测重复的行数据
#可以检测原始数据中哪些行是重复,重复则返回True,否则返回False
df.duplicated() 
  • 使用drop_duplicates()方法检测且删除重复的行数据
df.drop_duplicates(inplace=True)

异常值清洗

异常值是分析师和数据科学家常用的术语,因为它需要密切注意,否则可能导致错误的估计。 简单来说,异常值是一个观察值,远远超出了样本中的整体模式。

异常值在统计学上的全称是疑似异常值,也称作离群点,异常值的分析也称作离群点分析。异常值是指样本中出现的“极端值”,数据值看起来异常大或异常小,其分布明显偏离其余的观测值。异常值分析是检验数据中是否存在不合常理的数据。

  • 给定条件的异常数据处理
    • 自定义一个1000行3列(A,B,C)取值范围为0-1的数据源,然后将C列中的值大于其两倍标准差的异常值进行清洗
data = DataFrame(data=np.random.random(size=(1000,3)),columns=['A','B','C'])
#求出了C列数据的2倍标准差
twice_std = data['C'].std() * 2
ex = data['C'] > twice_std
indexs = data.loc[ex].index #取出了异常值对应行数据的行索引
data.drop(index=indexs,inplace=True) #在原始数据中将异常值对应的行数据进行清洗
  • 未给定条件的异常值处理

    • 标准差法

      • 又称为拉依达准则(标准差法),对某些异常数据进行剔除,以提高数据的准确性。

      • 适用场景:

        • 仅局限于对正态或近似正态分布的样本数据处理,且数据量级尽可能大会更加适用。
      • 工作原理:

        • 先假设一组检测数据存在异常数据,对其进行计算处理得到标准偏差σ(sigma),然后确定一个区间,认为凡超过这个区间的就是异常数据,则对该数据予以剔除。

        • 标准差本身可以体现因子的离散程度,是基于因子的平均值μ而定的。在离群值处理过程中,可通过用μ ± nσ来衡量因子与平均值的距离。

          • n通常使用3:这是一个相对比较保守的选择,也是基于统计学中正态分布的性质。
          在正态分布中,约68%的数据落在平均值(μ)加减一个标准差(σ)的范围内,约95%的数据落在平均值加减两个标准差的范围内,约99.7%的数据落在平均值加减三个标准差的范围内。因此,3倍标准差的阈值范围可以涵盖绝大部分的正态分布数据。
          
          • 将区间[μ - 3σ,μ + 3σ],的值视为正常值范围,在[μ - 3σ,μ + 3σ]外的值视为离群值

          Snip20230925_277

          #伪造了一组含有5个异常数据的样本
          iris_length = abs(np.random.randn(9999))
          #手动添加一些异常数据
          iris_length[44] = 54
          iris_length[33] = 67
          iris_length[22] = 56
          iris_length[11] = 87
          iris_length[55] = 49
          
          def std_opt(data):
              mean_value = data.mean()#均值
              std_value = data.std() #标准差
              m_min = mean_value - 3*std_value
              m_max = mean_value + 3*std_value
              return m_min,m_max
            
          m_min,m_max = std_opt(iris_length)
          
          for i in iris_length:
              if i < m_min or i > m_max:
                  print(i)
          
    • MAD法

      • 又称为绝对值差中位数法,是一种先需计算所有因子与中位数之间的距离总和来检测离群值的方法,适用大样本数据

      • 公式:设有平稳离散数据X=[x1,x2,...,xn],其数据中位数为X_median:

        Snip20230925_278
      • 应用场景:

        • 当数据呈对称分布或接近对称分布时MAD和标准差法效果相等或接近相等
        • 当数据为偏态分布,应选择众数或中位数等位置代表值,这时它们的代表性要比均值好。
        def median_opt(data):
            median = np.median(data)
            count = data.size
            a = (((data-median)**2).sum()/count)**0.5
            m_min = median - 3*a
            m_max = median + 3*a
            return m_min,m_max
        
        m_min,m_max = median_opt(iris_length)
        for i in iris_length:
            if i < m_min or i > m_max:
                print(i)
        
    • 四分位数法

      • 又叫做箱型图法,由最小值、下四分位值(25%),中位数(50%),上四分位数值(75%),最大值这5个关键的百分数统计值组成的。

      • 如何通过箱形图判断异常值呢?

        • 假设下四分位值为Q1,上四分位数值为Q3,四分位距为IQR(其中IQR=Q3-Q1),推导如下:

          Snip20230925_279

      • 应用场景:适用于数据分布非正态、存在偏斜或包含极端值的情况。尤其适用于对数值型数据的异常值检测和过滤。但在对称且近似正态分布的数据上也可以使用。

        import numpy as np
        
        def boxplot(data):
            # 下四分位数值、中位数,上四分位数值
            Q1, median, Q3 = np.percentile(data, (25, 50, 75))
            # 四分位距
            IQR = Q3 - Q1
            # 内限
            inner = [Q1-1.5*IQR, Q3+1.5*IQR]
            # 外限
            outer = [Q1-3.0*IQR, Q3+3.0*IQR]
            print('>>>内限:', inner)
            print('>>>外限:', outer)
            
            # 过滤掉极端异常值
            goodData = []
            for value in data:
                if (value < outer[1]) and (value > outer[0]):
                    goodData.append(value)
            
            return goodData
        
        data = [0.2, 0.3, 0.15, 0.32, 1.5, 0.17, 0.28, 4.3, 0.8, 0.43, 0.67]
        boxplot(data)
        

pandas数据分析高级操作

数据转换

数据替换

替换操作可以作用于Series和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'])
df.iloc[4,2] = None # 空数据
#重命名轴索引
df.rename(index = {'A':'AA','B':'BB'},columns = {'Python':'人工智能'}) 
#匿名函数应用索引替换
#原始数据的列索引不符合Python的命名规范,给其进行重命名(例如:Row ID改为Row_ID,Sub-Category改为Sub_Category)
df = pd.read_csv('superstore_dataset2011-2015.csv')
df.rename(columns=lambda x:x.replace(' ','_').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.replace(3,1024) #将3替换为1024
df.replace([0,7],2048) # 将0和7替换为2048
df.replace({0:512,np.nan:998}) # 根据字典键值对进行替换
df.replace({'Python':2},-1024) # 将Python这一列中等于2的,替换为-1024

数据映射

给Series中的一组数据提供另外一种表现形式,或者说给其绑定一组指定的标签或字符串。

  • 创建一个df,两列分别是姓名和薪资,然后给其名字起对应的英文名
dic_data = {
    'name':['zhangsan','lisi','wangwu','lisi'],
    'salary':[10000,12000,8000,12000]
}
df = pd.DataFrame(data=dic_data)

#映射关系表
dic = {
    'zhangsan':"jay",
    'lisi':'tom',
    'wangwu':'jerry'
}
df['e_name'] = df['name'].map(dic)

数据运算

map函数对Series数据的运算处理(map作为Series的运算工具)

  • 超过3000部分的钱缴纳50%的税,计算每个人的税后薪资
def after_sal(s):
    return s-(s-3000)*0.5
    
df['after_sal'] = df['salary'].map(after_sal)

提示:apply也可以像map一样充当运算工具,不过apply运算效率要远远高于map。因此在数据量级较大的时候可以使用apply。

apply函数对Series或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'])
df.iloc[4,2] = None # 空数据

#apply作用在Series中:可以对Series中的每一个元素进行运算处理
df['Keras'].apply(lambda x:True if x >5 else False) 
#apply作用在DataFrame中:可以对df中的行或列进行运算处理
def convert(x): # 自定义方法
    return (x.mean(),x.count())
df.apply(convert,axis = 1) 

applymap函数,DataFrame专有,对df中每一个元素进行运算处理

df.applymap(lambda x : x + 100) 

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'])



# 1、对Series执行多项计算(非聚合运算)
df['Python'].transform([np.sqrt,np.exp]) # Series处理


 # 2、对df执行不同计算
def convert(x):
    if x.mean() > 5:
        x *= 10
    else:
        x *= -10
    return x
df.transform({'Python':convert,'Tensorflow':lambda x:np.min(x),'Keras':lambda x:np.max(x)}) # DataFrame处理

案例应用

请转移到:报销处理、身份ID处理和数据映射文件夹中查看具体操作。

数学和统计方法

常用操作

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'])
df.count(axis=0) # 非NAN值的数量
df.max(axis = 0) #轴0最大值,即每一列最大值
df.min(axis) #默认计算轴0最小值
df.median(axis) # 中位数
df.sum(axis) # 求和
df.mean(axis = 1) #轴1平均值,即每一行的平均值
df.cumsum() # 累加
df.cumprod() # 累乘
df.std() # 标准差
df.var() # 方差
df.quantile(q = [0.2,0.4,0.8]) # 分位数
df.describe() # 查看数值型列的汇总统计,计数、平均值、标准差、最小值、四分位数、最大值
df.pct_change()#将每个元素与其前一个元素进行比较,并计算前后数值的百分比变化
df['Python'].rank()#对序列中的元素值排名,该函数的返回值的也是一个序列,包含了原序列中每个元素值的名次。如果序列中包含两个相同的的元素值,那么会为其分配两者的平均排名

索引标签、位置获取

df['Python'].argmin() # 计算最小值位置
df['Keras'].argmax() # 最大值位置
df.idxmax() # 最大值索引标签
df.idxmin() # 最小值索引标签

数据排序

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'])
# 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'])#先按Python,再按Keras排序

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

分箱操作

数据分箱(也称为离散分箱或分段)是一种数据预处理技术,用于减少次要观察误差的影响,是一种将多个连续值分组为较少数量的“分箱”的方法。说白了就是将连续型特征进行离散化。

  • 分箱的实现

    • 等距分箱
    import pandas as pd
    import numpy as np
    
    age = np.arange(1,31,step=1) #连续性特征
    #等距分箱
    ret = pd.cut(age,bins=4) #bins表示分箱的箱子个数(分组数量)
    ret.value_counts()
    
    #labels可以给每一个箱子起一个名字
    ret = pd.cut(age,bins=4,labels=['A','B','C','D']) 
    
    #将分箱后的结果重新赋值给age,则age就被离散化
    age = ret
    
    #可以给bins赋值一个列表,表示n个箱子的间距,就可以实现手动指定箱子的间距
    age = np.arange(1,31,step=1)
    ret = pd.cut(age,bins=[1,10,20,25,30]) #labels可以给每一个箱子起一个名字
    ret.value_counts()
    
    • 等频分箱
    x = np.random.random(size=(100,))
    ret = pd.qcut(x,4)
    ret.value_counts()
    
    ret = pd.qcut(x,4,labels=['A','B','C','D'])
    ret.value_counts()
    
  • cut和qcut的区别:

    • 1,cut: 按连续数据的大小分到各个桶里,每个桶里样本量可能不同,但是,每个桶相当于一个等长的区间,即:以数据的最大和最小为边界,等分成p个桶。
    • 2,qcout: 与cut主要的区别就是每个桶里的样本数量是一定的。
  • 给如下数据中的年龄制定对应的年龄段:

    df = DataFrame(data=np.random.randint(10,70,size=(20,)),columns=['年龄'])
    pd.cut(df['年龄'],bins=[9,20,40,70],labels=['9-20','20-40','40-70'])
    
  • 对于一组偏态数据,适合使用cut分箱还是qcut分箱?

    Pandas提供了两种主要的分箱方法:cut和qcut。这一选择取决于数据的特点和分析的需求。对于偏态分布的数据,由于数据不是均匀分布,而是倾向于在某一端聚集,使用qcut通常更为合适。这是因为qcut方法会使得每个箱子中的样本数量大致相等,从而可以有效处理偏态分布造成的数据倾斜问题。
    

随机抽样

  • take()
  • np.random.permutation()
df = pd.DataFrame(data=np.random.random(size=(100,4)),columns=['A','B','C','D'])
df.take(indices=np.random.permutation(4),axis=1).take(indices=np.random.permutation(100),axis=0)[0:20]

数据分组

  • 数据分类处理的核心:
    • groupby()函数
    • groups属性查看分组情况
df = pd.DataFrame({'item':['Apple','Banana','Orange','Banana','Orange','Apple'],
                'price':[4,3,3,2.5,4,2],
               'color':['red','yellow','yellow','green','green','green'],
               'weight':[12,20,50,30,20,44]})

#计算每种水果的平均价格
df.groupby(by='item').groups #groups返回分组结果
df.groupby(by='item').mean() #mean聚合操作只会对数值型的数据进行聚合
df.groupby(by='item').mean()['price']

#聚合推荐方式
mean_price_s = df.groupby(by='item')['price'].mean()

#将每种水果的平均价格汇总到源数据中
dic = mean_price_s.to_dict()
df['mean_price'] = df['item'].map(dic)
  • 使用groupby分组后,也可以使用transform和apply提供自定义函数实现更多的运算

    • apply和transform的区别:
      • transform返回的结果是经过映射后的结果
      • apply返回的是没有经过映射的结果
    def myMean(p):
        sum = 0
        for i in p:
            sum += i
        return sum / len(p)
    
    df.groupby(by='item')['price'].transform(myMean)
    df.groupby(by='item')['price'].apply(myMean)
    
  • agg实现对分组后的结果进行多种不同形式的聚合操作

df.groupby(by='item')['price'].agg(['max','min','sum'])

数据透视

透视表是一种可以对数据动态排布并且分类汇总的表格格式。或许大多数人都在Excel使用过数据透视表,也体会到它的强大功能,而在pandas中它被称作pivot_table。

pivot_table有四个最重要的参数index、values、columns、aggfunc

  • index参数:分类汇总的分类条件
    • 每个pivot_table必须拥有一个index。
df = pd.read_csv('./data/透视表-篮球赛.csv')
df.pivot_table(index=['对手','主客场'])
  • values参数:需要对计算的数据进行筛选
df.pivot_table(index=['主客场','胜负'],values=['得分','篮板','助攻'])
  • Aggfunc参数:设置我们对数据聚合时进行的函数操作
    • 当我们未设置aggfunc时,它默认aggfunc='mean'计算均值。
#正常用法
df.pivot_table(index=['主客场','胜负'],values=['得分','篮板','助攻'],aggfunc='sum')
#变种用法
df.pivot_table(index=['主客场','胜负'],aggfunc={'得分':'sum','篮板':'max'})
  • Columns:可以设置列层次字段
#获取所有队主客场的总得分
df.pivot_table(index=['主客场'],values='得分',aggfunc='sum')

#查看主客场下的总得分的组成元素是谁
df.pivot_table(index=['主客场'],values='得分',aggfunc='sum',columns='对手',fill_value=0)

项目案例1

手机销量分析:

import pandas as pd
data = pd.read_excel('./data/Phone.xlsx')
data.head()

#查看缺失数据的个数和占比
#查看缺失数据
for col in data.columns:
    null_count = data[col].isnull().sum()
    if null_count > 0:
        p = str(null_count / data[col].size * 100)+'%'
        print(col+':'+p)
        
#缺失值处理
data['年'] = data['订单日期'].dt.year
data['月'] = data['订单日期'].dt.month
#数据分箱:
#[0-16,17-26,27-36,37-49]
data['年龄段'] = pd.cut(data['年龄'],bins=[0,16,26,36,49])

#查看不同品牌手机的累计销量和累计销售额,且对累计销量进行降序
data.groupby(by='品牌')[['销售额','数量']].sum().sort_values('数量',ascending=False)

#查看不同品牌的不同型号数量
p_count_list = [] #品牌名称和品牌型号的数量
for p in data['品牌'].unique():
    #可以将p表示品牌的行数据
    p_df = data.loc[data['品牌'] == p]
    p_count = p_df['型号'].nunique() #品牌对应不同型号的数量
    p_count_list.append([p,p_count])
pd.DataFrame(p_count_list,columns=['品牌','型号数量'])

#查看不同月份的销量情况,哪些月份销量比较高
data.groupby(by='月')['数量'].sum().sort_values(ascending=False)

#不同年龄段的购买力
data.groupby(by='年龄段')['数量'].sum().sort_values(ascending=False)

#查看不同省份不同城市的购买力情况
data.pivot_table(index=['省份名字','城市名字'],values='数量',aggfunc='sum').sort_values('数量',ascending=False)

项目案例2

美国大选政治现金分析:

  • 加载数据
  • 指定数据截取,将如下字段的数据进行提取,其他数据舍弃
    • cand_nm :候选人姓名
    • contbr_nm : 捐赠人姓名
    • contbr_st :捐赠人所在州
    • contbr_employer : 捐赠人所在公司
    • contbr_occupation : 捐赠人职业
    • contb_receipt_amt :捐赠数额(美元)
    • contb_receipt_dt : 捐款的日期
  • 空值处理。可能因为忘记填写或者保密等等原因,相关字段出现了空值,将其填充为NOT PROVIDE
  • 异常值处理。将捐款金额<=0的数据删除
  • 新建一列为各个候选人所在党派party
  • 查看party这一列中有哪些不同的元素
  • 统计party列中各个元素出现次数
  • 查看各个党派收到的政治献金总数contb_receipt_amt
  • 将表中日期格式转换为'yyyy-mm-dd'。
  • 查看老兵(捐献者职业)DISABLED VETERAN主要支持谁
df = pd.read_csv('./data/usa_election.txt')
df = df[['cand_nm','contbr_nm','contbr_st','contbr_employer','contbr_occupation','contb_receipt_amt','contb_receipt_dt']]
df.head(5)
#空值处理。可能因为忘记填写或者保密等等原因,相关字段出现了空值,将其填充为NOT PROVIDE
df.fillna(value='NOT PROVIDE',inplace=True)
#- 异常值处理。将捐款金额<=0的数据删除
indexs = df.loc[df['contb_receipt_amt'] < 0].index
df.drop(index=indexs,inplace=True)
#新建一列为各个候选人所在党派party
#可以通过百度搜索,找到每一个候选人对应的党派
parties = {
  'Bachmann, Michelle': 'Republican',
  'Romney, Mitt': 'Republican',
  'Obama, Barack': 'Democrat',
  "Roemer, Charles E. 'Buddy' III": 'Reform',
  'Pawlenty, Timothy': 'Republican',
  'Johnson, Gary Earl': 'Libertarian',
  'Paul, Ron': 'Republican',
  'Santorum, Rick': 'Republican',
  'Cain, Herman': 'Republican',
  'Gingrich, Newt': 'Republican',
  'McCotter, Thaddeus G': 'Republican',
  'Huntsman, Jon': 'Republican',
  'Perry, Rick': 'Republican'           
 }
df['party'] = df['cand_nm'].map(parties)
#查看party这一列中有哪些不同的元素,- 统计party列中各个元素出现次数
df['party'].value_counts()
#查看各个党派收到的政治献金总数contb_receipt_amt
df.groupby(by='party')['contb_receipt_amt'].sum().sort_values(ascending=False)
#将表中日期格式转换为'yyyy-mm-dd'
months = {'JAN' : 1, 'FEB' : 2, 'MAR' : 3, 'APR' : 4, 'MAY' : 5, 'JUN' : 6,
          'JUL' : 7, 'AUG' : 8, 'SEP' : 9, 'OCT': 10, 'NOV': 11, 'DEC' : 12}
def func(x):
    day,month,year = x.split('-')
    month = months[month]
    return '20'+year+'-'+str(month)+'-'+day
df['contb_receipt_dt'] = df['contb_receipt_dt'].map(func)
#- 查看老兵(捐献者职业)DISABLED VETERAN主要支持谁
old_df = df.loc[df['contbr_occupation'] == 'DISABLED VETERAN']#提取老兵对应的行数据
old_df.groupby(by='cand_nm')['contb_receipt_amt'].sum().sort_values(ascending=False)
posted @ 2025-11-10 19:48  凫弥  阅读(9)  评论(0)    收藏  举报