python数据分析

468 阅读49分钟

Numpy

导包:import numpy as np

创建数组

  1. 创建一维数组:data1=np.array([1,2,3,4])
  2. 创建二维数组:data2=np.array([[1,2,3],[4,5,6]])
  3. 创建5行3列的二维的全零数组:data3=np.zeros(shape=(5,3))
  4. 创建5行3列的二维的全一数组:data4=np.ones(shape=(5,3))
  5. 创建全空数组(数据无限接近0但不是0,方便数学上的一些操作):data5=np.empty(shape=(5,3))
  6. 创建一个2*2的2维数组,值填充为7:data5=np.full((2,2),7)
  7. 创建3*3维对角矩阵::data5=np.eye(3,k=2)k>0对角线向右移k行,k<0对角线向左移k行
  8. 创建【start,end)步长为step的数组:np.arange(start,end,step)
  9. 创建开始端a,结束端b,分割成n个数据,生成线段:np.linspace(a,b,n)
  10. 生成a行b列的【0,1)的随机数:np.random.rand(a,b)
  11. 生成a行b列的【0,1)的具有正态分布的随机数:np.random.randn(a,b)
  12. 生成a行b列的low(包含)到high(不包含)的随机数:np.random.randint(low=0,high=0,size=(a,b),dtype=np.int)
  13. 生成5行3列的【0,1)之间的浮点数:
    • data9=np.random.random_sample(size=(5,3))
    • data9=np.random.random(size=(5,3))
    • data9=np.random.ranf(size=(5,3))
    • data9=np.random.sample(size=(5,3))
  14. np.random.choice(a=[],size=(a,b,c),p=[],replace=False)
    • a是一个列表,从这个列表中生成随机数据
    • size是生成的数组的维度
    • p是列表a中的数据生成概率,p的长度要和a一致,且相加等于1
    • 当replace为False时,生成的随机数据不能有重复值
    • np.random.choice(a=['a','b','c','d','e'],size=(5,3),p=[0.2,0.2,0.2,0.2,0.2],replace=False)

属性

  1. numpy1.reshape((a,b))修改数组尺寸,不修改原数组,返回新数组,新尺寸的数据是要和原数据量一样。
  2. numpy1.resize((a,b))直接操作原数组,不返回新数组
  3. numpy1.T数组转置
  4. numpy1.ndim 数组维度
  5. numpy1.shape 数组形状
  6. numpy1.size 数组所有元素个数
  7. numpy1.dtype 数组的数据类型
  8. numpy1.nbytes 数组占用空间大小
  9. numpy1.flat 获取数据任意数据类型的迭代器for item in numpy1.flat:print(item)
  10. numpy1.astype(type)将numpy1里的数据转换成type类型
  11. np.argmax(numpy1),np.argmin(numpy1):获取numpy1的最大和最小值的索引。当numpy1是二维数组时,参数axis确定是行/列比较

方法

统计分析

  1. 计算数据的平均值:numpy1.mean(arr,axis=None,dtype=None):计算数组的平均值,axis表示沿哪个轴进行计算,axis=1求(列之间的计算)每行平均值,axis=0,求(行之间的计算)每列的平均值,dtype表示返回结果的数据类型,默认为float64
  2. 计算数据的平均值:numpy1.median(arr,axis=None)参数axis和out含义与numpy.mean()相同
  3. 计算数组的标准差:numpy1.std(arr, axis=None, dtype=None, out=None): 参数axis、dtype和out的含义与numpy.mean()相同。
  4. 计算数组的方差:numpy1.var(arr, axis=None, dtype=None, out=None): 参数axis、dtype和out的含义与numpy.mean()相同。
  5. 计算数组的最小值:numpy1.min(arr, axis=None, out=None): 参数axis和out的含义与numpy.mean()相同。
  6. 计算数组的最大值:numpy1.max(arr, axis=None, out=None): 参数axis和out的含义与numpy.mean()相同。
  7. 计算数组的极值(最大值-最小值):numpy1.ptp(arr,axis=None,out=None):参数axis和out的含义与numpylmean()相同。
  8. 计算数组的元素之和:numpy1.sum(arr, axis=None, dtype=None, out=None): 参数axis、dtype和out的含义与numpy.mean()相同。
  9. 计算数组的元素乘积:numpy1.prod(arr, axis=None, dtype=None, out=None): 参数axis、dtype和out的含义与numpy.mean()相同。
  10. 计算数组的累积和:numpy1.cumsum(arr, axis=None, dtype=None, out=None): 参数axis、dtype和out的含义与numpy.mean()相同。返回二维数组。
  11. 计算累积和:np.add.accumulate(numpy1)对numpy1数组进行累积求和,功能上和cumsum类似。
  12. 计算数组的累积乘:numpy1.cumprod(arr, axis=None, dtype=None, out=None): 参数axis、dtype和out的含义与numpy.mean()相同。返回二维数组。
  13. 计算数组的加权平均:numpy1.average(a,weights)a:要计算加权平均的数组,weights与a中元素对应的权重数组,长度必须与a相同,
values = np.array([1, 2, 3, 4, 5])
weights = np.array([0.1, 0.2, 0.3, 0.2, 0.2])
np.average(values, weights=weights)

14. 数组裁剪:np.clip(a,a_min,a_max)或者a.clip(a_min,a_max):数组a里比a_min小的替换成a_min,比a_max大的替换成a_max。 15. 计算数组相邻元素之间的差值:numpy.diff(a,n,axis)

numpy.diff(a,n,axis)
    a:要计算差值的数组 (后面的-前面的)
    n:要计算差值的阶数,默认为1,n=2时计算相邻元素的二阶差值(即相邻差值的差值)
    axis:当数组是多维,axis=0时,计算行,下面行的数据减上面行的数据(每一列下面的数减上面的数);
    axis=1时,计算列,右边列的数据减左边列的数据(每一行右边的数减左边的数)

16. 沿给定轴返回数组的选定切片:numpy.compress(condition,a,axis)

numpy.compress(condition,a,axis)
condition:bool数组(掩码数组)
a:给定数组
axis:轴

a = np.array([[1, 2], [3, 4], [5, 6]])
a = array([[1, 2],
            [3, 4], 
            [5, 6]])
np.compress([0, 1], a, axis=0) 返回:array([[3, 4]]) 
axis=0按行切片,第二个是true,所以返回第二行

np.compress([False, True, True], a, axis=0) 
返回:array([[3, 4], [5, 6]]) 第二、三行是true,所以返回第二、三行

np.compress([False, True], a, axis=1) 
返回:array([[2], [4], [6]]) axis=1按列切片,第二列是true,返回第二列

np.compress([False, True], a) 没有axis参数,第二个是true,所以返回第二个元素

切片

  1. 单个索引:ndarray[index],多个索引:ndarray[ [index1,index2,...] ]。
  2. 一维数组切片:ndarray[start:end:step],获取一维数组ndarray的起始索引start和结束索引end,前闭后开,步长为step的数据。
  3. 多维数组切片
创建一个3维数组,shape=(2,3,5)
[[[ 1  2  3  4  5]
  [ 6  7  8  9 10]
  [11 12 13 14 15]]

 [[16 17 18 19 20]
  [21 22 23 24 25]
  [26 27 28 29 30]]]

data=np.arange(1,31).reshape(2,3,5)
data[第一维的切片,第二维的切片,第三维的切片,...]
行切片,先获取第一维数组:data[0:1],获取三维数组里的第一个元素,是一个二维数组
再获取这个二维数组的前两行,就是[0:2],data[0:1,0:2]就能得到这个三维数组的最前两行

列切片,获取所有行的前两列,[第一维的所有列,第二维的所有列,第三维的前两列]
data[:,:,0:2]


data=np.array([[73,65,34,1,37],
                 [66,4,30,52,15]])
获取65和1这两列
print(data[:,1:4:2])第一维度获取所有数据,第二维度获取第2列到第4列,步长为2

无规律切片,返回73,65,1,37,4,30
data[ [行索引1,行索引2...], [列索引1,列索引2...] ],获取data[行索引1,列索引1][行索引2,列索引2]的值

  1. 堆叠
    • 垂直堆叠:np.vstack(numpy1,numpy2),要求两个数组具有相同的列数
    • 垂直堆叠:np.hstack(numpy1,numpy2),要求两个数组具有相同的行数

掩码数组

salarys = np.array(
      [       ['赵一', '男', 14297],['钱二', '女', 17917],['孙三', '男', 8404],
       ['李四', '男', 14907],['周五', '男', 18865],['吴六', '女', 11377],
       ['郑七', '男', 10212],['王八', '男', 15351],['冯九', '女', 19071],
       ['程十', '女', 18461],['褚天', '女', 10787],['卫地', '女', 13322],
       ['蒋玄', '女', 16202],['沈黄', '女', 12288],['韩宇', '男', 14422],
       ['杨宙', '男', 13485]],
    dtype=object
)
筛选条件
mask=salarys[:,1]=="女" #掩码数组
salarys[mask] #输出性别等于女的数据
salarys[mask,2].mean()计算性别等于女的第三列的平均数

文件

  1. np.savetxt(file="aa.txt",arr1,fmt="%d",delimiter="\t",newline="\n",header='',footer='',comments='')

    • file是要保存的文件名
    • arr1是要保存的numpy数组
    • fmt是要保存的数据类型(%d,%.2f,%18e)
    • delimiter是列之间的分隔符
    • newline是行之间的分隔符
    • header是文件开头的字符串
    • footer是文件结尾的字符串
    • comments是注释内容
  2. np.loadtxt=(file="aa.txt",dtypeobject,delimiter=",",skiprows=1,encoding="utf-8",usecols=(a,b),unpack=False,max_rows=n)

    • skipwors=n跳过n行
    • usecols(a,b)加载第a列和第b列
    • unpack=true/false是否对加载的数据进行解耦操作
    • max_rows=n最多读取n行
    • converters={n:func}定义函数func,对第n列的所有数据进行func处理
    data1,data2,data3=np.loadtxt("stock.csv",
           delimiter=",",
           dtype=object,
           usecols=[1,2,3],#加载第1,2,3列
           skiprows=1,#路过第一行
           converters={0,date2str}#对第0列的日期数据进行对应的函数处理
           unpack=True#解耦操作,返回一个包含三个子数组的二维数组,可以返回多个变量arr1,arr2,arr3
           )
    print(data1,data2,data3)读取的第1,2,3列分别存储到data1,data2,data3变量中
    

日期时间函数

导包

from datetime import datetime,timedelta
import time
  1. 获取当前时间时间戳(单位:秒)
    • time.time()
    • datetime.now():2024-07-26 13:43:30.599165
  2. datetime构造时间日期对象
    • current=datetime(year,month,day,hour,minute,second)
    • current.weekday():周几(周一:0,周日:6)
  3. 字符串解析成日期类型:datetime.strptime(date_str,format) datetime1=datetime.strptime("2017-12-13T10:53:49.875Z","%Y-%m-%dT%H:%M:%S.%fZ")
  4. 获取日期的年月日
    • 年:datetime1.year
    • 月:datetime1.month
    • 日:datetime1.day
    • 时:datetime1.hour
    • 分:datetime1.minute
    • 秒:datetime1.second
    • 周几:datetime1.weekday()(周一是0,周日是6)

求解线性方程组

np.linalg.solve(A,a)

A:表示方程组的系数矩阵

a:表示等号右侧的常量系数或矩阵

x-2y+z=0
2y-8z=8
-4x+5y+9z=-9
A=np.array([[1,-2,1],[0,2,-8],[-4,5,9]])系数矩阵
a=np.array([0,8,-9])右侧的常量系数
C=np.linalg.solve(A,a) 返回三个解

自定义通用函数

  1. 自定义一个普通函数
  2. 使用np.frompyfunc包装器函数,将普通函数封闭为numpy通用函数,np.frompyfunc(func,参数个数,返回值个数)
1.自定义一个普通函数
def func(x):
    return x**2

2. 使用np.frompyfunc包装器函数,将普通函数封闭为numpy通用函数,np.frompyfunc(func,参数个数,返回值个数)

np_square=np.frompyfunc(func,1,1)
np_square(n)

Pandas

导包

import pandas as pd
from pandas import Series,DataFrame

Series

创建Series

Series是一维数组

  1. Series(data,index,name,dtype)
    • data:数据
    • index:行索引
    • name:列索引
    • dtype:数据类型
数据是列表,自定义行索引
s1=Series=([1,3,14,521],index=['第一个数','第二个数','第三个数','第四个数'],name='')
数据是字典
s2=Series({'b':3,'a':4,'c':1,'d':2},index=['a','b','c','d'],name='age')

2. 通过字典构建一个Series数组,键是行索引,值是值

dict1={'a':1,'b':2,'c':3,'d':4}
s1=Series(dict1)

Series属性

  1. s1.index:获取行索引
  2. s1.size:获取Series的数据数量
  3. s1.values:直接获取值,不获取索引
  4. s1.is_unique:判断Series里的所有元素是否都是唯一的(没有重复),返回True/False

Series方法

  1. s1[行索引],行索引支持单个、多个以及切片,s1[start:end]左闭右开
  2. s1.sort_index(ascending):按照索引排序
  3. s1.sort_value(axcending):按照数据排序
  4. s1.unique():对Series里的数据进行去重
  5. s1.describe():输出Series的描述性统计
  6. s1.isin([集合]):遍历s1中的所有元素,判断每个元素是否在集合中,返回bool类型的Series
  7. s1.notnull():检察s1中,非空值返回True,空值(None,NaN)返回False

DataFrame

创建DataFrame

DataFrame是二维数数组

  1. DataFrame(data,columns=[],index=[])使用字典创建df(data是数据,columns是列索引,index是行索引)
data1={'name'['apple','egg','watermelon'],'color'['red','yellow','green'],'num':[30,40,50]}
#直接创建columns是列标题,index是行标题,列名数超过原数据则创建空列,行索引不能超过行数
df1=pd.DataFrame(data1,columns['name','num','color'],index['No.1','No.2','No.3')

2. DataFrame(data,columns=[],index=[])使用numpy生成的二维数据创建(data是numpy生成的2行4列的二维数组,行索引['b','a'],列索引是['age','name','gender','addr'])

pd1=DataFrame(np.arange(8).reshape(2,4),index['b','a'],columns['age','name','gender',addr'])

属性

  1. pd1:获取原数据
  2. pd1.values:获取值
  3. df1.index:获取行索引
  4. df1.columns:获取列索引
  5. df1.axes:获取行及列索引
  6. df1.shape:获取数据维度
  7. df1.T:数据转置
  8. df1.col_name/pd1[col1,col2...]:获取某一列或几列的数据
  9. df1.describe():获取统计变量(个数、中位数、标准差、方差、最小最大值)
  10. df1.rename(columns={old1:new1,old2:new2},index={old1:new1},inplace=True/False)
  11. df1.describe():描述性统计
  12. df1.sum(axis=0/1);df1.mean();df1.median()按行或列求和,平均值,中位数
  13. df1.nunique(drop=True/False):计算DataFrame或Series中唯一值的数量,适用统计某一列不同值的个数,drop=True:忽略NaN,drop=False:NaN会被统计为一个值
  14. df1[列名].unique():列出df1中某一列的去重后的值

方法

一、 排序

  1. df1.sort_index(axis=0,ascending=false,inplace=true/false):根据索引进行排序

    • axis=0:根据行索引,axis=1:根据列索引
    • ascending=true/false:是否升序
    • inplace=true是直接在原数据排序,false返回新的数据
  2. df1.sort_values(by=,axis=,ascending,inplace=)

    • by:指定列名或行名(根据某一列/行的数据进行排序),可以指定多列
    • axis=0,行依据,行排序,这时by只能指定列名;axis=1,列依据,列排序,这时by只能指定行名
    • ascending:true/false是否升序
    • inplace=true/false;true是直接在原数据排序,false返回新的数据
    • na_position:指定缺失值(NaN)的位置,'first' 表示将缺失值放在前面(默认),'last' 表示将缺失值放在后面。
 df = pd.DataFrame({'A': [3, 1, 2], 'B': [1, 3, 2]})
 # 按照 A 列升序排序
 sorted_df = df.sort_values(by='A')  
 # 按照 A 列降序排序
 sorted_df_desc = df.sort_values(by='A', ascending=False)  
 # 按照 A 列升序、B 列降序排序
 sorted_df_multiple = df.sort_values(by=['A', 'B'], ascending=[True, False]) 
 # axis=1:列排序,by=只能指定行名,以行索引为1的数据进行降序排序
 df1.sort_values(by=1,axis=1,ascending=False)
  1. 日期时间函数 pd.date_range(start:起始日期,end:结束日期,periods:生成的日期数量,freq:指定日期序列的频率)

    • freq:日期频率,'D'(天),'W'(周),'Q'(季),'Y'(年),'5D'(每5天),'2M'(每2个月) pd.date_range(start='20210101',periods=6,freq='D')
  2. 获取列,将DataFrame的一个列或行获取为一个Series数据

    • df1[列名],df1[[列名1,列名2,...]]获取多列数据
    • df1[start,end,step]获取行数据,起始索引和结束索引,step为步长
  3. 通过标签获取数据,使用loc

使用布尔进行筛选
#单列做索引
df1[df1['a']>10],a列大于10的数据
df1[df1['a'].isin([])]筛选包含特定值的数据
df1[df1['a'].str.contains(r'正则表达式')]
df1[df1['a'].between(x,y)]筛选a列x,y之间的数据,闭区间
#多列做索引
使用 & | ~ 逻辑与或非
df1[(df1['a']>10) & (df1['b']<10)]筛选两个条件都为true的数据
df1[(条件1) | (条件2) ]筛选条件1或条件2为true的数据
df[ ~(条件1) ]筛选条件1不成立的数据

#使用query方法筛选数据
筛选visits大于150且category等于A的
df.query('visits > 150 and category == "A"')


df1.loc[[行标签],[列标签]]
df1=DataFrame({'A'[3,3,2],'B'[3,4,2],'C'[9,1,8]},index['No1','No2','No3'])
df1[df.'A'>2]=0#找到A列,所有大于2的数据赋值为0
df1.loc[['No1','No1']]#行标签
df1.loc['No1',['A','C']]#行列标签混合
df1.loc[:,['A','C']]#全选行但不全选列
df1.loc[['No1','No2'],:]#全选列但不全选行
  1. 通过位置获取数据,使用iloc
df1.iloc[ 行start:end,step,列start:end:step]
df1.iloc[[行索引],[列索引]]
df1.iloc[3]#第四行
df1.iloc[1:3,2:4]#第二到三行,三到四列
df1.iloc[[1,2,3],[2,3]]#第2,3,4行,3,4列
  1. 删除df的行/列数据

    df.drop([行或列名],axis=0/1,inplace=True/False)
    
    • axis=0:删除行[]里只能是行名,axis=1:删除列[]里只能是列名
    • inplace:表示是否直接操作原始数据
  2. 频数统计

    针对Series里每个元素的值返回每个元素的值以及出现的次数

    df1[列名].value_counts(sort,ascending,dropna,normalize=True)
    df1.column.value_count()
    df1.apply(pd.Series.value_counts,axis)
    sort:True/False是否对频率进行排序
    ascending:True升序排序
    dropna:True/False是否对NaN的次数进行统计
    normalize=True/False是否返回每个值占总量的比例
    fruits = {
          "A": ["apple", "banana", "apple", "orange", "banana"],
          "B": ["banana", "apple", "apple", "banana", "orange"],
      }
      df_fruits = pd.DataFrame(fruits)
      print(df_fruits)
      print(df_fruits.apply(pd.Series.value_counts, axis=1).fillna(0))
      print(df_fruits.apply(pd.Series.value_counts, axis=0).fillna(0))
      #normalize=True返回每个值占问题的比例
      pd['col1'].value_counts(normalize=True).apply(lambda x:format(x,'.2f'))
              A       B
      0   apple  banana
      1  banana   apple
      2   apple   apple
      3  orange  banana
      4  banana  orange
         apple  banana  orange
      0    1.0     1.0     0.0
      1    1.0     1.0     0.0
      2    2.0     0.0     0.0
      3    0.0     1.0     1.0
      4    0.0     1.0     1.0
              A  B
      apple   2  2
      banana  2  2
      orange  1  1
    
  3. 缺失值统计

    删除空值
    函数形式:dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)
    参数:
    axis:轴。0'index',表示按行删除;1'columns',表示按列删除。
    how:筛选方式。‘any’,表示该行/列只要有一个以上的空值,就删除该行/列;‘all’,
        表示该行/列全部都为空值,就删除该行/列。
    thresh:非空元素最低数量。int型,默认为None。如果该行/列中,
        非空元素数量小于这个值,就删除该行/列。
    subset:子集。列表,元素为行或者列的索引。如果axis=0或者‘index’,subset中元
        素为列的索引;如果axis=1或者‘column’,subset中元素为行的索引。由subset
        限制的子区域,是判断是否删除该行/列的条件判断区域。
    inplace:是否原地替换。布尔值,默认为False。如果为True,则在原DataFrame上进
        行操作,返回值为None。
    
    填充空值
    DataFrame.fillna(value=None, method=None, axis=None, inplace=False,
        limit=None, downcast=None)
    参数说明:
    value: 标量、字典、序列或 DataFrame。用于填充缺失值的值。
    method: {‘backfill’, ‘bfill’后向填充, ‘pad’, ‘ffill’前向填充, None}。填充方法。
    axis: {0 or ‘index’, 1 or ‘columns’}。沿着哪个轴填充。为1,填充前/后一列的数据
    inplace: bool,默认为 False。如果为 True,则在原地修改数据。
    limit: int,默认为 None。如果方法是 pad 或 ffill,则这是连续的填充的最大数
        量;如果方法是 backfill 或 bfill,则这是连续的填充的最大数量。
    downcast: dict,默认为 None。一个字典,其键是列名,其值是 [numpy]数据类型。
        如果可能,将尝试将列转换为这些类型。
    
    
    
    indices=series1.isnull(),判断缺失值,返回bool类型的Series掩码值
    s[indices]获取缺失值  s[~indices]获取有效值
    Series.isnull().sum()统计缺失值数量
    Series.dropna(axis=表示要删除缺失值的行列依据{0:行,1:列}
                  how="any/all" {any:有缺失值就删除,all:所有值都是缺失值才删除},默认any
                  inplace=True/False)删除缺失值
    Series.fillna(value) 用value填充缺失值
    Series.mean(skipna=True)聚合统计时跳过缺失值
    基于统计分析的结果,对缺失值进行合理的处理
    avg=s.mean(skipna=True)    s.fillna(avg)
    median=s.median(skipna=True)    s.fillna(median)
    Series.ffill()  使用前一个非NaN数据填充
    Series.bfill()  使用后一个非NaN数据填充
    
    any(iterator)给定的可迭代对象中有任意一元素转化为boolTrue,返回结果即为True
    all(iterator)给定的可迭代对象中所有元素转化为boolTrue,返回结果才为True
    
    
  4. 数据行列转换

    df1.stack(dropna=True/False):列索引追加到行索引,二维转向一维。stack默认删除nan的值,dropna=False保留空值

    df1.unstack():行索引转向列索引,一维转向二维。 二者默认操作的是最内层

    • 单层列索引数据旋转到行:df1.stack()列索引变成行索引,行索引变成了多层索引。二维表向一维表转换,类似透视和逆透视。
    scores = DataFrame(
    np.random.randint(60, 100, (4, 3)),
    index="张三-李四-王舞-赵柳".split("-"),
    columns=["语文", "数学", "音乐"],
    )
    scores
    scores.stack().index
    
    • 多层列索引旋转到行:df1.stack(level=[0,1],future_stack=True)
    
        index = pd.MultiIndex.from_product(
        [['第一季度', '第二季度', '第三季度', '第四季度'], ['销售', '研发', '市场']],
        names=['季度', '部门']
        )
    
        # 创建多层级索引的列索引
        columns = pd.MultiIndex.from_product(
            [['收入', '支出'], ['实际', '预算']],
            names=['类型', '类别']
        )
    
        # 创建 DataFrame
        data = np.random.randint(1000, 5000, size=(12, 4))
        df = pd.DataFrame(data, index=index, columns=columns)
        df
        #将列索引的层级为0和1转换成行索引,future_stack=True,不加会有警告
        df.stack(level=[0,1],future_stack=True)
    
  • 转换成行索引的逆序:df1.unstack()

    df1.stack(level=[0,1].unstack(level=[2,3]))
    df1原本有各有两层行索引和列索引,将df1的第一、第二层列索引转换成行索引后,
    变成第三、第四层行索引,再使用unstack()将那两层行索引逆序成列索引。
    
  1. 重新构建索引:DataFrame.reindex(),和索引重命名的区别是,重构是重构索引,重命名索引是对于索引标签进行重命名。
obj.reindex(
    index=None,
    *,
    axis: 'Axis | None' = None,
    method: 'ReindexMethod | None' = None,
    copy: 'bool | None' = None,
    level: 'Level | None' = None,
    fill_value: 'Scalar | None' = None,
    limit: 'int | None' = None,
    tolerance=None,
)

主要参数

  • index:新索引标签数组或 Index 对象。
  • method:填充方法,可选。可以是 'backfill'/'bfill''pad'/'ffill''nearest'
  • copy:默认为 True,如果为 False,则尽量避免复制底层数据。
  • level:对 MultiIndex 有效。级别(或级别名),用作重索引的索引。
  • fill_value:在重新索引期间引入的缺失值的填充值。
  • limit:向前或向后填充值时的最大数量。
  • tolerance:容差范围内的最大距离。如果索引变化超过这个范围,则不进行填充。 对于Series

索引的重新赋值,不会和原索引匹配也不会改变原数据。

s = Series(np.random.randint(0, 100, size=(5,)), index=list("abcde"))
name_idx = ["张三", "李四", "王舞", "赵柳", "孙七"]
s.index = name_idx#行索引重新赋值

使用reindex()重构索引,会和原索引匹配,和原索引不一致的值会变成nan。

重新(重构)索引
s = Series(np.random.randint(0, 100, size=(5,)), index=list("abcde"))
name_idx = ["张三", "李四", "王舞", "赵柳", "孙七"]
s.reindex(name_idx)
五个值都是nan,对于nan,可以用fill_value填充。

11. 行列索引重命名

DataFrame.rename() 函数用于对 DataFrame 的行索引和列标签进行重命名。它可以通过提供字典或函数的方式来修改索引或列标签。这个函数特别有用,当你需要清理数据或使列名和索引更具可读性时。

DataFrame.rename(
    mapper: 'Renamer | None' = None,
    *,
    index: 'Renamer | None' = None,
    columns: 'Renamer | None' = None,
    axis: 'Axis | None' = None,
    copy: 'bool | None' = None,
    inplace: 'bool' = False,
    level: 'Level | None' = None,
    errors: 'IgnoreRaise' = 'ignore',
)

关键参数说明:

  • mapper:函数或字典,用于重命名行索引或列标签。
  • index:函数或字典,用于重命名行索引。
  • columns:函数或字典,用于重命名列标签。
  • axis:用于指定应用重命名的轴。0'index' 表示行索引,1'columns' 表示列标签。
df = pd.DataFrame({
    'old_name1': [1, 2, 3],
    'old_name2': [4, 5, 6]
})

#1使用字典对列重命名{"原列名":"新列名","":"",}
df.rename(columns={"old_name1":"col1","old_name2":"col2"})
#2使用mapper函数更新列名
df.rename(columns=str.upper)#将英文字符转换成大写

使用函数对行索引重命名
df.rename(index = lambda x : "row_" + str(x) )


# 5、mapper + axis 组合实现行、列索引更新
df.rename(mapper=str.upper, axis=0) # axis=0, 默认值,表示使用 mapper 函数对行索引重命名
df.rename(mapper=str.upper, axis=1)

12. 更改索引层级顺序:obj.reorder_level(order=[1,0],axis=0/1)

  • order=[],多层索引level从最多层向里的从0开始的递增序列,order可以改变层级顺序
  • axis=0调整行索引顺序,axis=1调整列索引顺序
# 创建多层级索引的行索引
index = pd.MultiIndex.from_product(
    [["第一季度", "第二季度", "第三季度", "第四季度"], ["销售", "研发", "市场"]],
    names=["季度", "部门"],
)

# 创建多层级索引的列索引
columns = pd.MultiIndex.from_product(
    [["收入", "支出"], ["实际", "预算"]], names=["类型", "类别"]
)

# 创建 DataFrame
data = np.random.randint(1000, 5000, size=(12, 4))
df_data = pd.DataFrame(data, index=index, columns=columns)
df_data

image.png

  1. 删除重复值

DataFrame.drop_duplicates(subset=None, keep='first', inplace=False, ignore_index=False, keep_original_index=False)

参数:

  • subset (列标签列表, 可选):

    • 描述:要用来判断重复值的列的标签列表。默认情况下,会考虑所有列。
    • 示例df.drop_duplicates(subset=['col1', 'col2']) 将仅根据 col1 和 col2 列中的值来判断重复项。
  • keep (字符串, 默认 'first'):

    • 选项:{'first', 'last', False}

    • 描述:确定保留哪些重复项(如果有)。

      • 'first':保留每组重复项中的第一个出现。
      • 'last':保留每组重复项中的最后一个出现。
      • False:删除所有重复项。
    • 示例df.drop_duplicates(keep='last') 将保留每组重复项中的最后一个出现。

  • inplace (布尔值, 默认 False):

    • 描述:是否直接在原 DataFrame 上进行修改。
    • 示例df.drop_duplicates(inplace=True) 将直接修改原始 DataFrame df
  • ignore_index (布尔值, 默认 False):

    • 新版本:1.1.0 版本新增
    • 描述:如果为 True,则结果索引将被重置为 0, 1, ..., n-1。
    • 示例df.drop_duplicates(ignore_index=True) 将重置 DataFrame 的索引。
  • keep_original_index (布尔值, 默认 False):

    • 新版本:1.3.0 版本新增
    • 描述:如果为 True 并且 ignore_index=False,则保留原始索引。
    • 示例df.drop_duplicates(keep_original_index=True) 在删除重复项时保留原始索引。

返回值:

  • 如果 inplace=False(默认),返回一个没有重复行的新 DataFrame。
  • 如果 inplace=True,直接修改原 DataFrame。
  1. 长宽格式的转换

宽格式是指:一列或多列作为标识变量(id_vars),其他变量作为度量变量(value_vars),直观上看,这种格式的数据比较宽,举个列子,列名是:id1、id2、var1、var2、var3,一行可以表示多个度量变量的值。

而长格式是指在一行中,除了标识变量(id_vars),其他列是variable和name,从宽格式转换为长格式,会使得数据行数增加,直观上看,这种格式的数据比较长,举个例子,列名是:id1、id2、variable、value,一行只表示一个度量变量的值。

在宽格式转换为长格式的过程中,宽格式中的多个度量变量进行了分裂,使得长格式中的每一行,实际上,只表示一个度量变量的值。

  • 整合数据,逆透视表(unpivot),把数据从宽格式转换为长格式 DataFrame.melt(self,id_vars=None,value_vars=None,var_name=None,value_name='value',col_level=None)

    参数注释:

    • id_vars:作为标识变量的列
    • value_vars:作为值的列
    • var_name:默认值是variable,对长格式中度量变量的列名所在的列进行命名
    • value_name:默认值是value,对长格式中度量变量的列值所在的列进行命名
    • col_level:如果列是multiindex,使用这个level的索引进行melt
df=pd.DataFrame({'idA':{0:'a',1:'b',2:'c'},
                'varB':{0:1,1:3,2:5},
                'varC':{0:2,1:4,2:6}})
                
  idA  varB  varC
0   a     1     2
1   b     3     4
2   c     5     6

id列是idA,度量变量是varB和varC,得到如下长数据
df.melt(id_vars='idA',value_vars=['varB','varC'])

  idA variable  value
0   a     varB      1
1   b     varB      3
2   c     varB      5
3   a     varC      2
4   b     varC      4
5   c     varC      6

  • 数据透视 把数据从长格式转换为宽格式,返回按照特定的index或column重塑的DataFrame,不带聚合函数,可以处理文本数据: DataFrame.pivot(self,index=None,columns=None,values=None)

    参数注释:

    • index:用户创建新DataFrame的索引,相当于分组列,相同索引的行称为一个小分组。
    • columns:根据columns指定的列值来创建新DataFrame的列名,使用该参数指定的列来创建结果的列名。
    • values:和columns对应,表示相应列的列值,用于填充结果列的列值。

    重塑数据的流程:

    1. 根据index的唯一值进行分组
    2. 把columns指定的列的唯一值作为结果的列名,即列的值作为结果的列名
    3. 把values对应的列值作为新列名的值,即把列的值作为结果中对应列的值

  idA variable  value
0   a     varB      1
1   b     varB      3
2   c     varB      5
3   a     varC      2
4   b     varC      4
5   c     varC      6
使用pivot把长格式转换为宽格式,按照idA列进行分组,
把variable的列值作为结果的列名,把values的列值作为结果列的值:

df.pivot(values='value',columns='variable',index='idA')

variable  varB  varC
idA                 
a            1     2
b            3     4
c            5     6
重塑的数据包含行索引idA,列标签varB和varC,其中variable是列标签的name。


Pandas时间日期处理

  1. pd.to_datetime(date_str):时间日期字符串转换成时间日期(当参数是列表时,返回列表) timestr_list=["2024-7-26 13:44:59","2024-7-26 14:55:20"]

  2. data.date.dt.month:data是一个dataframe,date是这个df是日期列,dt:这是Pandas中用于访问datetime类属性的方法。它允许我们从日期时间类型的序列中提取部分,如年、月、日、小时、分钟等;month:这是通过 dt 访问器获取具体日期时间组成部分的一个属性,这里是指月份。它会返回一个整数序列。

  3. 在Pandas库中,strftime 是一个方法,用于将日期时间对象格式化为字符串。这个方法通常用在 datetime 类型的 Series 或者 DataFrame 的某一列上,它可以按照指定的格式将日期时间转换成字符串形式。 strftime 方法是 dt 属性的一部分,它允许你定义一个格式字符串来控制日期时间的输出格式。这个功能基于 Python 标准库中的 datetime.strftime 方法。

    import pandas as pd
    # 创建一个包含日期时间的DataFrame
    data = {'date': ['2024-01-01', '2024-02-15', '2024-03-10']}
    df = pd.DataFrame(data)
    df['date'] = pd.to_datetime(df['date'])  # 将字符串转换为日期时间类型
    
    # 使用strftime方法格式化日期
    df['formatted_date'] = df['date'].dt.strftime('%Y-%m-%d')
    print(df)
    

strftime 支持多种格式化选项,例如:

  • %Y:完整的四位数年份,如 2024。
  • %y:两位数的年份,如 24。
  • %m:月份,如 01。
  • %d:日期,如 01。
  • %H:小时,如 14。
  • %M:分钟,如 30。
  • %S:秒,如 59。
  • %w: 数字化的星期,周日为0,周一为1。
  • %a:本地简化星期名称,如 Wed。
  • %A:本地完整星期名称,如 Wednesday。
  • %b:本地简化月份名称,如 Jan。
  • %B:本地完整月份名称,如 January。
  • %x:本地日期表示方式。
  • %X:本地时间表示方式。
  • %%:百分号 %。
  1. pd.date_range():批量创建时间日期数据
    pd.date_range(start=None, end=None, periods=None, freq=None, normalize=False, 
              name=None, inclusive=None, tz=None, **kwargs)
    
    
    start=None: 开始时间  '2021-1-1'  ,'2021/1/2' , '2021-5' ,'2021'
    end=None: 结束时间
    periods=None: 时间间隔和单独的 start 或 end 配合。
    freq=None : 生成日期范围的频率,默认按天取 。  Y/y 年 m/M 月 D 日  H 小时
        D: Calendar Day (日频率),按天
        B:Business Day (工作日频率),不包含周末和节假日
        W:Weekly (周频率),生成每周的日期范围,默认为每周日。
        M/ME:Month End (月末频率),生成每月最后一天的日期范围。
        MS: Month Start (月初频率),生成每月第一天的日期范围。
        A/YE: Year End (年末频率),生成每年最后一天的日期范围。
        AS/YS: Year Start (年初频率),生成每年第一天的日期范围。
        H/h: Hourly (小时频率),生成每小时的日期范围。
        T/min: Minutely (分钟频率),生成每分钟的日期范围。
        S/s: Secondly (秒频率),生成每秒的日期范围。
        
    normalize=False:是否将时间戳标准化到午夜(即当天的00:00:00)。
    normalize=False不会将生成的日期范围中的每个时间戳标准化为该天的午夜时间。
    normalize=True会将生成的日期范围中的每个时间戳标准化为该天的午夜时间。
    name:日期对象的名称
    inclusive:取值有 {"both", "neither", "left", "right"} 代表开始和结束时间边界值是否能取到,
    或者只取一边
       both:start 和 end 两个时间边界值都包含
       neither:start 和 end 两个时间边界值都能不包含
       left:包含 start 开始时间,不包含 end 结束时间
       right:不包含 start 开始时间,包含 end 结束时间
    
    #批量创建时间日期数据
    print(f'批量日期{pd.date_range(start="20240501",end="20240715",freq="D",inclusive="left")}')
    #开始时间向后取10个日期
    print(pd.date_range(start="2024-07-01", periods=10))
    #结束日期向前取10个日期
    print(pd.date_range(end="2024-07-01", periods=10))
    
    print(pd.date_range("2020-01-01", "2024-07-25", freq="YS"))
    
    print(f'获取 `2020-01-01~2024-07-25` 之间所有7月末的日期数据{pd.date_range(
    start="2020-01-01",end= "2024-07-25",freq="YE-JUL")}')
    
    print(f"""获取 `2020-01-01~2024-07-25` 之间所有7月末8月初的日期数据{pd.date_range(
    start="2020-01-01",end= "2024-07-25",freq="YS-AUG").union(
    pd.date_range(start="2020-01-01",end= "2024-07-25",freq="YE-JUL")
    )}""")
    
    
    print(f'获取 `2024-01-01~2024-07-25` 之间,每隔3个月为频率 所有月末的日期数据
    {pd.date_range(start="2023-01-01", end="2024-07-25", freq="3ME")}')
    
    print(f'获取 `2024-07-01~2024-07-25` 之间所有周六的日期数据{pd.date_range(
    start="2024-07-01",end="2024-07-25",freq="W-SAT")}')
    
    

文件

Pandas读取csv文件
 函数用于从 CSV 文件中读取数据并创建一个 DataFrame
 pd.read_csv(file,delimiter,header,names,index_col,usecols,skiprows,nrows,encoding,dtype)
 file:要读取的文件路径(字符串)、URL 或文件型对象。这是必填参数。
 delimiter:指定分隔符,默认为 ',' 。
 header:指定用作列名的行号,默认为 0(即第一行)。如果没有列名,可以设置为 `None` 。
 names:如果 header=None,可以通过此参数提供列名列表。
 index_col:用作索引的列号或列名。
 usecols指定要读取的列,可以是列号、列名或它们的列表。
 skiprows:要跳过的行数(从文件开头开始计数)。
 skipfooter:要跳过的行数(从文件末尾开始计数)。
 nrows:要读取的行数。
 encoding:指定文件的编码格式,例如 'utf-8'。
 dtype:指定列的数据类型,可以是字典形式,为每列指定类型。

 date=pd.read_csv("stock.csv",index_col="date",parse_dates=True)

 - 读取csv文件,以date列为行索引列,parse_dates=True表示会尝试将索引列中的值(date列)解析为日期类型索引,以便进行日期相关操作。

 xxx.loc['年'] , xxx.loc['年月'] , xxx.loc['年月日'] , 
 xxx.loc['年月日 时'] 年 月 日 时 分 秒等等
 xxx.loc[start:end] 使用切片,获取日期介于 start 与 end 之间的数据

 #获取2024年数据
 #print(data.loc["2024"])
 #获取2024年7月数据
 #print(data.loc["2024-07"])
 #获取start-end之间的数据
 #print(data.loc["2024-06":"2024-07"])
Pandas读取excel文件
  • io: 字符串、文件路径或文件类对象,用于指定要读取的 Excel 文件的位置。可以是一个本地文件路径或者是一个支持 read() 方法的对象。
  • sheet_name: 可以是整数(表示工作表索引),字符串(表示工作表名称),列表(包含多个工作表名或索引)或者 None(默认为 0,即第一个工作表)。如果设置为 None,则会读取所有的表格,并返回一个字典形式的结果。
  • header: 指定作为列名的行号,默认是 0,即第一行作为列名。如果文件没有列名行,可以设置为 None。
  • names: 列名列表。如果指定了此参数,那么 header 参数将被忽略。
  • index_col: 用作行索引的列编号或列名。如果是 None(默认值),则自动产生一个默认的整数索引。
  • usecols: 要解析的列。可以是列名的列表,例如 ['A', 'B'] 或者列号的列表 [0, 1,2],也可以是一个函数,该函数接收列名/列号并返回布尔值来决定是否读取该列。
  • dtype: 指定每一列的数据类型。可以是类型名的字典,比如 {'a': np.float64, 'b': np.int32}。
  • skiprows: 需要跳过的行数(从文件开头开始计数),或者是一个包含要跳过的特定行号的列表。
  • nrows: 需要读取的行数(从文件顶部开始计算)。
  • na_values: 指定一组额外的字符串应该被视为缺失值。这可以是一个字典,键是列名,值是应该被视为缺失值的字符串集合。
  • parse_dates: 是否尝试将数据解析为日期时间。可以是列表 [1, 2] 表示尝试将第 1 和第 2 列解析为日期;True 表示尝试将所有看起来像日期的列都解析为日期;或者是一个字典,指定如何组合多列创建一个新的日期列。
  • date_parser: 用来解析日期的函数。如果 parse_dates 设置了,这个函数会被用来转换日期字符串到 datetime 对象。
  • thousands: 用来分隔千位的字符,通常用于处理数字中的逗号或其他分隔符。
  • comment: 如果设置了这个选项,那么该字符之后的内容会被视为注释并忽略。
  • encoding: 用于解码文件内容的编码方式。默认情况下,Pandas 将使用 UTF-8 编码。
  • engine: 使用哪个引擎来读取 Excel 文件。可选值有 'xlrd' 和 'openpyxl'。对于 .xls 文件通常使用 'xlrd',而 .xlsx 文件则推荐使用 'openpyxl'。
  • converters 参数是 pandas.read_excel() 函数中的一个选项,它允许你为 Excel 文件中特定的列指定自定义转换函数。这个参数接受一个字典,其中键是列名(或者列索引),值是一个函数,该函数将应用于该列的每一个元素。
Python连接MySQL数据表
  1. 安装sqlalchemy和pymysql模块 pip install sqlalchemy pymysql -i https://mirrors.cloud.tencent.com/pypi/simple 导入模块:from sqlalchemy import create_engine, text
    • create_engine:创建新的SQLAlchemy Engine实例。连接数据库的起点,负责管理数据库连接与数据库的交互。
    • text:用于创建一个SQL语句文本对象。与SQLAlchemy的执行引擎结合使用,以执行原生SQL语句。
  2. engine=create_engine("mysql+pymysql://{username}:password@ip:port/databasename?charset=utf8")
  3. result=con.execute(text(sql),{'var1':v1})用v1代替sql语句里的var1
  4. result.keys()获取所有列名
from sqlalchemy import create_engine, text

#create_engine("mysql+pymysql://{username}:password@ip:port/databasename?charset=utf8")
engine = create_engine("mysql+pymysql://root:123456@localhost:3306/book?charset=utf8")
#使用SQLAlchemy Engine 创建数据库连接
with engine.connect() as con:
    #定义sql语句,使用':'来定义变量,以便填充
    sql="select * from book where id=:user_id"
    #通过text对象处理sql语句并执行
    result=con.execute(text(sql),{"user_id":9})#字典类型填充变量
    #获取所有列名
    result.keys()
    #解析执行结果
    for row in result.all():
        print(row)

Pandas读取MySQL数据库

pandas提供 pd.read_sql(sql.connection) 函数用于连接并加载mysql数据,它直接返回DataFrame

  • sql:要执行的sql语句
  • connection: mysql数据库连接对象
  1. 导包:from sqlalchemy import create_engine

  2. 连接对象变量名=create_engine("mysql+pymysql://{username}:password@ip:port/databasename?charset=utf8")

  3. pd.read_sql(sql,con,index_col=None,coerce_float=True,parse_dates=None,columns=None)

    • sql:sql语句
    • con:mysql连接的对象
    • index_col:加载mysql数据表中的哪一列作为DataFrame索引
    • coerce_float:数据类型自动为float
    • pd.read_sql(sql,con,index_col='列名')
  4. pd.read_sql_table('表名称',连接对象,columns=[0,2]),默认读取一张表所有内容

Pandas存储到mysql

使用pd.to_sql(name,con,if_exists,index=True,index_label=None,chunksize=None,dtype=None,method=None)

  • name :表名称
  • con:SQLAlchemy创建的连接对象
  • if_exists:如果数据表存在采取何种策略,取值有,{'fail', 'replace', 'append'}
  • index:将索引写为一列 ,使用 index_label的值为列名称,=False时不保存行索引
  • dtype:标量或字典,指定DataFrame保存到MySQL数据库时各个列的采用哪种数据类型

如果需要存储自定义索引,先df.reset_index().to_sql()

索引是数字对象或日期对象,可以直接用index=True存储 df1.to_sql("df1",con,index=True,if_exists="replace")

注意:将pandas数据存储到mysql要注意数据类型
mysql没有list[],dict{}等数据类型,mysql不认识list,dict,就会报错
所以一般要将list,dict转换成str,再存储
用 .dtypes属性查不具体,因为str,list,dict等都是object
df1.iloc[0].apply(type)所以使用apply查询第一行的数据类型

转换数据类型
xx.astype(具体的类型)
dict{'列名称':数据类型}
df1.astype(str)全部转为str
df2=df1.astype({"要转换的列名":str})

元素级运算函数

  1. map(func,*iterables,na_action):将func函数应用到可迭代对象iterables的每个元素上,传入Series或DataFrame返回Series/DataFrame,第一个函数参数可以是字典,传入Series返回键对应的值。
    • na_action:取值'ignore',忽略NaN值。
  2. apply(func,axis,raw,result_type):函数主要作用于DataFrame的行或列数据
    • func作用于df的行数据或列数据的具体函数
    • axis:决定函数作用于行或列(axis=0:行之间运算;axis=1:列之间运算)
    • raw:bool,决定是否将行/列数据以ndarray形式传递给func
  3. applymap(func):将一个函数作用于Series和DataFrame的每个元素上,不区分行或列,返回DataFrame,pandas版本大于2.1.0不推荐,使用df.map平替。

分组函数

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

DataFrame的groupby函数,返回类型是DataFrameGroupby,而Series的groupby函数,返回类型是SeriesGroupBy。

by:用于分组的列名或函数 。可以是一个列名、一个函数、一个列表或一个字典
axis:分组轴。如果 axis=0(默认值),则沿着行方向分组;如果 axis=1,则沿着列方向分组。
level:在多层索引的情况下,用于指定分组的级别。
as_index:是否将分组键作为索引返回。如果 as_index=True(默认值),则返回一个带有分组键作为索引的对
象;否则返回一个不带索引的对象。
sort:是否对分组键进行排序。如果 sort=True(默认值),则对分组键进行排序;否则不排序。
group_keys:是否在结果中包含分组键。如果group_keys=True(默认值),则在结果中包含分组键;否则不含。
squeeze:是否压缩返回结果。如果 squeeze=True,则尝试压缩返回结果;否则不压缩。(已废弃)
observed:是否仅使用观察到的类别进行分组。仅适用于类别类型数据。
dropna:是否删除包含缺失值的行。如果 dropna=True(默认值),则删除包含缺失值的行;否则保留。

    data={'A':[1,2,3,4],'B':[5,6,7,8],'C':['X','X','Y','Y']}]
    df=pd.DataFrame(data)
    groupby简单用法:df.groupby(分组依据1,2...)[列1,列2...].聚合函数()
    df.groupby('C')['A'].mean()计算每个类别中A列的平均值。分组依据会变成行索引。
    
    在pandas中,groupby结合agg函数可以对分组后的数据进行聚合计算。
    agg函数允许您对每个分组应用一个或多个聚合函数。
    (sum:求和,mean:平均值,median:中位数,min:最小值,max:最大值,std:标准差,var:方差,count:非空值计数
    data = {'Group': ['A', 'A', 'B', 'B', 'A', 'B'],
            'Value': [10, 20, 30, 40, 50, 60]}
    df = pd.DataFrame(data)
    result = df.groupby('Group').agg({'Value': ['sum', 'mean', 'max', 'min']})
    
    #自定义聚合函数
    def custom_agg(x):return x.max() - x.min()
    df.groupby('group')['column'].agg(custom_agg)
    
    #groupby和filter结合使用,先对数据进行分组,然后对分组后的结果筛选
    #按category分组,然后筛选平均visits大于150的组
    grouped = df.groupby('category').filter(lambda x: x['visits'].mean() > 150)
    
    #transform方法可以帮助我们在保持原始DataFrame结构的同时进行分组计算。类似窗口函数
    # 使用transform计算每个category的平均visits,并筛选大于平均值的行
    df['mean_visits'] = df.groupby('category')['visits'].transform('mean')
    filtered_df = df[df['visits'] > df['mean_visits']]



    #groupby('Group')按照 'Group'列进行分组。
    #agg({'Value': ['sum', 'mean', 'max', 'min']})对 'Value'列应用 'sum'(求和)、 
    #'mean'(平均值)、 'max'(最大值)和 'min'(最小值)这四个聚合函数。

    data = {'Group': ['A', 'A', 'B', 'B', 'A', 'B'], 
        'Value1': [10, 20, 30, 40, 50, 60],
        'Value2': [15, 25, 35, 45, 55, 65]} 
    df = pd.DataFrame(data)
    # 对 'Value1' 列计算求和与平均值,对 'Value2' 列计算最大值和最小值 
    result = df.groupby('Group').agg({
        'Value1': ['sum', 'mean'],
        'Value2': ['max', 'min'] 
        }) 
    print(result)
    按照 'Group' 列进行分组,然后对 'Value1' 列分别应用求和和平均值的聚合函数,
    对 'Value2' 列分别应用最大值和最小值的聚合函数。
    #对A列进行分组,并将每组中C列的值用分号隔开。
    result=df.groupby('A').agg({'C':lambda x:';'.join(x)})
    #直接重命名  agg(新列名=(列名,统计方式))
    agg(合计=('用户名','sum'))
    
    分组聚合后有多重索引,重置索引 reset_index(),然后赋值新列名 df1.columns=[]

Pandas拼接合并

1. pd.merge()

功能类似于 SQL 中的连表查询,支持 inner、left、right、outer 等各种连表查询,合并时默认匹配相同的列名。

pd.merge(
    left: 'DataFrame | Series',
    right: 'DataFrame | Series',
    how: 'MergeHow' = 'inner',
    on: 'IndexLabel | AnyArrayLike | None' = None,
    left_on: 'IndexLabel | AnyArrayLike | None' = None,
    right_on: 'IndexLabel | AnyArrayLike | None' = None,
    left_index: 'bool' = False,
    right_index: 'bool' = False,
    sort: 'bool' = False,
    suffixes: 'Suffixes' = ('_x', '_y'),
    copy: 'bool | None' = None,
    indicator: 'str | bool' = False,
    validate: 'str | None' = None,
)
  • left:要合并的左侧DataFrame或Series。
  • right:要合并的右侧DataFrame或Series。
  • how:合并的方式,默认为'inner'。其他选项包括:
    • 'left':左连接,保留左侧DataFrame中的所有行。
    • 'right':右连接,保留右侧DataFrame中的所有行。
    • 'outer':外连接,保留两个DataFrame中的所有行。
    • 'corss':笛卡尔积,左右两表进行笛卡尔积操作结果。
  • on:用于合并的列名或索引级别。如果没有指定且left和right都是DataFrame,将会使用列名的交集。
  • left_on:左侧DataFrame中用于合并连接时匹配的列名。如果left是Series,将会忽略该参数。
  • right_on:右侧DataFrame中用于合并连接时匹配的列名。如果right是Series,将会忽略该参数。
  • left_index:是否使用左侧DataFrame的索引进行合并,默认为False。
  • right_index:是否使用右侧DataFrame的索引进行合并,默认为False。
  • sort:是否对合并后的数据进行排序,默认为False。
  • suffixes:合并后列名冲突时的后缀,默认为('_x', '_y')。
  • indicator:用于在合并结果表中显示数据来源(both/left_only),bool类型
  • validate:参数对需要合并的两张表的关联字段所对应的合并规则进行验证,如果验证不对则代码报错,字符串类型。
    • one_to_one|1:1:检查合并键在左右数据是否是一对一
    • one_to_many|1:m:检查合并键在左右数据是否是一对多
    • many_to_one|m:1:检查合并键在左右数据是否是多对一
    • many_to_many|m:m:检查合并键在左右数据是否是多对多
  • left_index/right_index:bool类型,左右两表连接时,是否使用相同的行索引进行合并,通常两个都是 true,使用left_index就不能使用left/right_on pd.merge(df3,df4,left_index=True,right_index=True,indicator=True)
df1 = DataFrame({"key": list("aabbabc"), "data1": np.arange(7) * 10})
df2 = DataFrame({"key": list("abd"), "data2": np.arange(1, 4) * 100})
#inner使用相同列名
pd.merge(df1,df2,how='inner')
pd.merge(df1,df2,how='left')
pd.merge(df1,df2,how='right')
pd.merge(df1,df2,how='outer')#outer,左右两张表的并集
pd.merge(df1,df2,how='cross')
#suffixes=('a','b')相同列名加的后缀
pd.merge(df1,df2,how='cross',suffixes=("aa","bb"))

df3 = DataFrame({"key1": list("aabbabc"), "data1": np.arange(7) * 10})
df4 = DataFrame({"key2": list("abd"), "data2": np.arange(1, 4) * 100})
#left_on:指定左表连接匹配的字段 str/list
#right_on:指定右表连接匹配的字段 str/list
pd.merge(df3,df4,left_on='key1',right_on='key2',how='inner')


# 2、使用多列进行表合并
df_left = DataFrame(
    {
        "key1": ["foo", "foo", "foo", "bar", "bar"],
        "key2": ["one", "two", "one", "one", "two"],
        "Lvalue": [10, 20, 30, 40, 50],
    }
)
df_right = DataFrame(
    {"key3": ["bar", "foo", "foo"], "key4": ["one", "two", "one"], "Rvalue": [6, 7, 8]}
)
df_left
df_right
pd.merge(df_left,df_right,left_on=["key1","key2"],right_on=["key3","key4"],how="inner")

image.png

2.pd.concat()

用于将多个 Series 或 DataFrame 沿指定轴方向连接起来,可以理解为数据在列/行方向的拼接,函数参数如下:

pd.concat(
    objs: 'Iterable[Series | DataFrame] | Mapping[HashableT, Series | DataFrame]',
    *,
    axis: 'Axis' = 0,
    join: 'str' = 'outer',
    ignore_index: 'bool' = False,
    keys: 'Iterable[Hashable] | None' = None,
    levels=None,
    names: 'list[HashableT] | None' = None,
    verify_integrity: 'bool' = False,
    sort: 'bool' = False,
    copy: 'bool | None' = None,
)

核心参数解释:

  1. objs:需要连接的Series或DataFrame的迭代器(例如列表或字典)。
  2. axis:沿着哪个轴进行连接,默认为0(行)。可选值有1(列)。
  3. join:连接的方式,默认为'outer'(外连接)。其他选项包括'inner'(内连接)。
  4. ignore_index:是否忽略原有的索引,默认为False。如果设置为True,则生成新的整数索引。
  5. keys:用于创建层次化索引的键序列,默认为None。如果传递,将使用这些键作为最外层索引。
  6. levels:用于创建层次化索引的特定级别,默认为None。
  7. names:新层次化索引的名称列表,默认为None。
  8. verify_integrity:检查新连接的对象是否有重复索引,默认为False。如果设置为True,则在连接前会进行检查。
  9. sort:是否在连接时对数据进行排序,默认为False。此参数仅在连接类型为'inner'时生效。
  10. copy:默认为None,是否在内存中复制数据。

三个Series image.png

image.png

keys参数作为最外层索引

image.png

df11=pd.DataFrame(np.arange(12).reshape(3,4),columns=['a','b','c','d'])
df12=pd.DataFrame(np.arange(12,24).reshape(3,4),columns=['a','b','c','d'])
df13=pd.DataFrame(np.arange(24,36).reshape(3,4),columns=['a','b','c','d'])
#纵向合并,ignore_index=True是不考虑表原来的idnex
df14=pd.concat([df11,df12,df13],axis=0,ignore_index=True)
df15=pd.concat([df11,df12,df13],axis=1,join='outer')#横向合并
print('纵向合并结果')
print(df14)
print('横向合并结果')
print(df15)

3. left.combine_first(right)

实现两个数据集的补充合并功能,使用right(右边)数据对left(左边)数据补充的一种合并方式。

  • left,right是Series或DataFrame
  • 以左边 非nan 为主,可理解为连表查询中的主表,不需要补充
  • 如果 左边数据为nan ,右边不是,用右边的数据对左边数据进行补充
  • 如果 左边没有而右边有的数据,则直接用右边数据对左边数据进行补充

用right里的数据补充left里行索引一样的数据,如果left里没有数据或为nan则补充,没有则不补充。

image.png

  1. 数据比较

obj1.compare(obj2):实现两个数据集的比较功能

obj1、obj2 可以是 pandas 中的 Series 或者 DataFrame

obj.compare(
    other: 'DataFrame' | 'Series',
    align_axis: 'Axis' = 1,
    keep_shape: 'bool' = False,
    keep_equal: 'bool' = False,
    result_names: 'Suffixes' = ('self', 'other'),
)
关注非NaN的值,如果是非NaN值,表示同一位置数据发生了变化

关键参数解释:

  • other:另一个用于比较的DataFrame或Series。
  • align_axis:指定对齐的轴,默认为1(列对齐)。如果设置为0,则按行对齐。
  • keep_shape:是否保持输出结果的原始形状,默认为False。如果设置为True,则结果会保留原始对象的形状。
  • keep_equal:是否在输出结果中保留相等的元素,默认为False。如果设置为True,则结果中相等的元素也会显示出来。

image.png

image.png

Pandas中的多层索引

Pandas中 Series 和 DataFrame 的索引主要有以下两类

  • index:行索引
  • columns:列索引

针对以上行索引和列索引,又可以细分为 单层索引和多层索引,以下为Padnas中常用的创建索引的方法:

  • pd.Index()
  • pd.date_range()
  • pd.MultiIndex.from_arrays() 从数组创建多层索引对象
  • pd.MultiIndex.from_tuples() 从元组创建多层索引对象
  • pd.MultiIndex.from_frame() 从 DataFrame 创建多层索引对象
  • pd.MultiIndex.from_product() 从笛卡尔积创建多层索引对象
#从元组
pd.MultiIndex.from_tuples(
    [('a','one'),('a','two'),('a','three'),('b','one'),('b','two'),('b','three')]
)
#从数组
pd.MultiIndex.from_arrays(
    [
        ['a'  ,'a'  ,'a'    ,'b'  ,'b'  ,'b'    ],
        ['one','two','three','one','two','three']
    ]
)

#从DataFrame
frame = DataFrame(
    [
        ('a','one'),
        ('a','two'),
        ('a','three'),
        ('b','one'),
        ('b','two'),
        ('b','three')
    ],
    columns=["编号","区域"],
)

#从笛卡尔积 2*3
pd.MultiIndex.from_product(
    [
        ["a","b"],
        ["one","two","three"]
    ]
)

Series重置索引

pandas 针对 Series 数据结构提供 Series.reset_index() 函数用于重置Series的索引,将索引转换为列数据。函数参数如下

Series.reset_index(
    level: 'IndexLabel | None' = None,
    *,
    drop: 'bool' = False,
    name: 'Level' = <no_default>,
    inplace: 'bool' = False,
    allow_duplicates: 'bool' = False,
)

核心参数说明:

  • level:可选参数,默认为None。指定要转换成列数据的索引是哪一层。指定要重置的索引级别。如果Series是多级索引(MultiIndex),可以通过此参数选择特定级别进行重置。可以是单个级别、多个级别或级别名称。如果未指定,则重置所有级别。
  • drop:布尔值,默认为False。如果为True,则不会将索引作为列插入到Series中。索引将被删除,数据将以默认整数索引进行索引。
  • name:可选参数,指定新列的名称。如果没有提供,将使用原索引的名称。
  • inplace:布尔值,默认为False。如果为True,则在原地修改Series,而不是返回修改后的副本。
  • allow_duplicates:布尔值,默认为False。如果为True,允许新列的名称与现有列名称重复。
Series重置单层索引
  1. name参数指定新列名

image.png

  1. drop参数删除成为新列的索引列

image.png

创建多层索引
# 创建多层索引
multi_index = pd.MultiIndex.from_arrays(
    [
        ["一班"] * 5 + ["二班"] * 5,#第一层索引
        range(1000, 1010),#第二层索引
    ],
    names=["班级", "学号"],#两层索引对应的名字
)
# 创建随机数据 30~100 整数模拟学生成绩
data = np.random.randint(30, 100, size=(10,))

# 基于多层索引及随机数据创建 Series
mul_level = Series(data, index=multi_index,name="分数")
mul_level

image.png

多层索引重置时,默认多层索引全部转换为列数据,新增的数据列变成从0开始递增的整数 mul_level.reset_index(level=0,name="分数")

将level对应的那一层索引转换成数据列

image.png

drop=True将转换后的数据列删除。

mul_level.droplevel(level=0):将level对应的那一层索引删除

DataFrame重置索引

pandas 针对 DataFrame 数据结构提供 DataFrame.reset_index() 函数用于重置 DataFrame 的索引,将索引转换为列数据。函数参数如下

DataFrame.reset_index(
    level: 'IndexLabel | None' = None,
    *,
    drop: 'bool' = False,
    inplace: 'bool' = False,
    col_level: 'Hashable' = 0,
    col_fill: 'Hashable' = '',
    allow_duplicates: 'bool | lib.NoDefault' = <no_default>,
    names: 'Hashable | Sequence[Hashable] | None' = None,
)
  • level:指定要重置的索引级别。可以是单个级别、多个级别或级别名称。如果 DataFrame 是多级索引(MultiIndex),可以通过此参数选择特定级别进行重置。默认为 None,表示重置所有级别。
  • drop:布尔值,表示重置索引后是否将索引丢弃。
    • True:表示丢弃索引数据。
    • False:默认值,表示重置索引后,索引将作为列/行数据添加到DataFrame。
  • inplace:布尔值,表示是否直接修改原 DataFrame,而不是返回修改后的副本。
  • col_level:默认值为 0,用于指定在多级索引(MultiIndex)中索引列插入到哪个级别,默认为0,表示是最外层。
  • col_fill:默认值为空字符串,用于指定在多级索引(MultiIndex),新索引列名称的填充方式。
  • allow_duplicates:布尔值,是否允许新列的名称与现有列名称重复,True表示允许。
  • names:指定重置索引后的新列名称。可以是单个名称或名称列表。如果为 None,则使用原索引名称。

设置索引

pandas提供 DataFrame.set_index() 函数用于将一列或者多列设置为 DataFrame 的行索引。参数如下

DataFrame.set_index(
    keys,
    *,
    drop: 'bool' = True,
    append: 'bool' = False,
    inplace: 'bool' = False,
    verify_integrity: 'bool' = False,
)

参数解释

  1. keys
    • 说明:指定将要设置为索引的列或列的列表。
    • 类型:label or list of labels
    • 示例:keys='column_name'keys=['column1', 'column2']
  2. drop
    • 说明:是否从 DataFrame 中删除已设置为索引的列。默认为 True,表示删除这些列。如果为 False,则保留这些列在 DataFrame 中。
    • 类型:bool
    • 默认值:True
    • 示例:drop=False
  3. append
    • 说明:是否将新设置的索引追加到现有索引中。如果为 True,则保留现有索引,并将新索引追加到现有索引后面。默认为 False,表示替换现有索引。
    • 类型:bool
    • 默认值:False
data = {
    '姓名': ['张三', '李四', '王五', '赵六'],
    '年龄': [28, 34, 29, 40],
    '部门': ['销售', '研发', '市场', '销售']
}
employee = pd.DataFrame(data)
employee.set_index("姓名")
employee.set_index(keys=["部门","姓名"])

数据分箱

数据分箱,也经常被称为 数据离散化、数据分桶、数据分段 ,是可以将连续数据转换为离散类别的一种数据处理技术。通过将连续数据划分为几个区间或“箱”(bin),每个区间内的数据点都会被分配到相应的“箱”中,这样可以将连续数据转换为分类数据。

常见的数据分箱方式主要有以下三种

  • 自定义分箱:数据分段/分组区间由用户自行定义
  • 等宽度分箱:数据分段/分组时,保证每组数据的跨度或宽度一致
  • 等深/等频分箱:数据分段/分组时,保证每组数据样本量一致,或按指定的比例分布

pandas 针对以上三种不同的数据分箱方式,提供了以下两个函数进行实现

  • pd.cut()
  • pd.qcut()

自定义分箱

age=np.random.randint(0,100,size=10)100-100之间的随机数

#自定义分箱
pd1=pd.Series(age,name='age')
#自定义分箱的边界
bins=[0,18,34,59,100]#(0-18]一个区间,(18-34]一个区间
#对age进行自定义分箱
age_cut=pd.cut(pd1,bins=bins)
#添加分组标签label=[]
age_cut=pd.cut(age,bins=bins,labels=["少年", "青年", "中年", "老年"])
age_cut


#对分箱后的数据进行频数统计
age_cnt=age_cut.value_counts()
#频数统计后排序
age_cnt.sort_values(ascending=False)
age_cnt.sort_index(ascending=False)

等宽分箱

width_cut=pd.cut(age,bins=4)#bins=n给定分组数量,n组
width_cut.value_counts().sort_index()

等深/等频分箱

#age样本分成5组
qcut=pd.qcut(age,q=5)
qcut.value_counts()
#按指定比例分组  第一组:0-40%,第二组:40%-80%,第三组:80%-100%
qcut=pd.qcut(age,q=[0,0.4,0.8,1.0])
qcut.value_counts()
数据分箱的意义
  1. 数据处理、清洗、分析及可视化

  2. 查看数据分布情况:非常直观的将连续性数据进离散化处理,便于观察数据在各个区间的分布情况和占比情况

  3. 异常值检测:连续数据离散化处理后,可根据离散化后的结果进行异常值检测

# 故意在数据中添加一些异常值
age[0] = 130
age[1] = 150

# 数据自定义分箱 ---> 频数统计 ---> 结果排序
pd.cut(age, bins=[0, 18, 34, 59, 100, np.inf]).value_counts()