当前位置:   article > 正文

笔记本 - 数据分析百宝箱_vgrmfc

vgrmfc

Numpy

一、基本操作:

属性:

improt numpy as np

生成数组:

array=np.array([[1,2,3],[2,3,4]],dtype=np.int/float)

array.npim: 几维的数组

array.shape: 几行几列;

array.size : 数组内几个元素

a=np.zeros/ones( (3,4) ) 生成一个全部是1的三行四列的矩阵

a=np.arange(10,20,2) 生成一个10到20,步长为2的数组

a=np.arrange(12).reshape((3,4)) 重新定义shape

a=linspace(1,10,5)生成一个从1到10的线段

a=np.array([10,20,30]); b=np.arrange(4); c=a+b ;print© 可以进行数学基本运算

print(b<3)将数组内数字进行判断,返回ture或flase

乘法:c=a*b(逐个相乘) c_dot=np.dot(a,b) ==c_dot=a.dot(b):矩阵相乘

生成随机的矩阵:

a=np.random.randint((2,4))

数组操作:
reshape(a, newshape[, order])在不更改数据的情况下为数组赋予新的形状。
ravel(a[, order])返回一个连续的扁平数组。
ndarray.flat数组上的一维迭代器。
tile(A, reps)通过重复A代表次数来构造一个数组。
delete(arr, obj[, axis])返回一个新的数组,该数组具有沿删除的轴的子数组。
unique(ar[, return_index, return_inverse, …])查找数组的唯一元素
resize(a, new_shape)返回具有指定形状的新数组。
删除举例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KCm0cOpV-1681776922723)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1643893309568.png)]

重复举例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z18qJ2hR-1681776922725)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1643893602277.png)]

数组形状:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XA9uqZVm-1681776922726)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1643893869571.png)]

过滤掉零:
a = np.array((0, 0, 0, 1, 2, 3, 0, 2, 1, 0))
>>> np.trim_zeros(a)
array([1, 2, 3, 0, 2, 1])
  • 1
  • 2
  • 3
数学方法:

先生成一个随机的数组:

a=np.random.randint((2,4))

方法名解释:
np.sum(a,axis=1)在第一行寻求总和(最大、最小)
np.argmin(a)寻找最小最大的索引
np.mean(a)求平均值
np.cumsum(a)累加
np.concatenate((arr,arr),axis=0/1)数组拼接,按行或者按列
np.sin()/cos()/tan()求正弦余弦正切值
np.amin()和np.amax()指定轴的最大最小值
np.ptp()数组元素的最大值最小值的差
np.median()数组中的中位数
np.std()数组元素的标准差
np.var()方差
np.nonzero(a)输出a数组中非0的数的索引
np.sort(a)从小到大逐行进行排序
np.transpose(a)矩阵转置
np.clip(a,最小值,最大值)按照你给定的最小值、最大值进行数组截取
索引:

A=np.arrange(3,15).reshape((3,4))

print(A[1] [1]):输出第一行第一列,一维数组就是是直接索引;

print(A[2,1])输出第二行第一列的一个数

还可以运用切片:A[ : , 1]:每行的第一列数

for column in A.T:
    print(A.T)  #转置后输出行,也就是按列输出
for item in A.flat:
    print(item)		#将数组里每一个数字单个输出
  • 1
  • 2
  • 3
  • 4
合并和分割:

合并:

A=np.array([1,1,1]) ; B=np.arrray([2,2,2])

print(np.vstack((A,B))) :上下合并,变成两行

print**(np.hstack((A,B)**)) :左右合并,就一行

A[ : , np.newaxis] :纵向合并,一行三列分成三行一列

np.concatenate( (A,B,B,A) ,axis=0) :纵向和横向合并

分割:

A=np.arrange(12).reshape((3,4))

np.split(A,2,axis=0) : axis=0就是指定的行 axis=1是指定的列

split只能等量分割,用 np.array_split(A,3,axis=1)是不等量分割

np.vsplit( (A,3) ) 横向分割,hsplit是纵向分割

Numpy的常用函数

where:

where是一种条件函数,可以指定满足条件与不满足条件位置对应的填充值:

a = np.array([-1,1,-1,0])
np.where(a>0, a, 5) # 对应位置为True时填充a对应元素,否则填充5
  • 1
  • 2
nonzero, argmax, argmin

这三个函数返回的都是索引,nonzero返回非零数的索引,argmax, argmin分别返回最大和最小数的索引:

a = np.array([-2,-5,0,1,3,-1])

np.nonzero(a)
a.argmax()
a.argmin()
  • 1
  • 2
  • 3
  • 4
  • 5
any, all
any`指当序列至少存在一`True`或非零元素时返回`True`,否则返回`False
all`指当序列元素全为 `True`或非零元素时返回`True`,否则返回`False
  • 1
  • 2
累乘累加

cumprod, cumsum分别表示累乘和累加函数,返回同长度的数组,diff表示数组中的每一个元素和前一个元素做差,由于第一个元素为缺失值,因此在默认参数情况下,返回长度是原数组减1

a = np.array([1,2,3])
a.cumprod()
a.cumsum()
np.diff(a)
  • 1
  • 2
  • 3
  • 4
含缺失值的计算

因为数组里面含有缺失值,所以使用函数返回的也是缺失值,所以我们要过滤掉这些缺失值

target = np.array([1, 2, np.nan])
np.nanmax(target)  #过滤掉nan计算最大值
np.nanquantile(target, 0.5)
  • 1
  • 2
  • 3
矩阵计算

向量内积:

a = np.array([1,2,3])
b = np.array([1,3,5])
a.dot(b)
  • 1
  • 2
  • 3

向量范数和矩阵范数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IyxuQiL1-1681776922727)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647175424022.png)]

matrix_target =  np.arange(4).reshape(-1,2)

np.linalg.norm(matrix_target, 'fro')
np.linalg.norm(matrix_target, np.inf)
  • 1
  • 2
  • 3
  • 4

Pandas:

提取原数据:用 .arrayto_numpy 提取数据

排除缺失值:一般的聚合函数都有 skipna 关键字,指定是否要排除缺失数据,默认值为 True

  • 注意:NumPy 的 meanstdsum 等方法默认不统计 Series 里的空值

常用方法:

函数描述
count统计非空值数量
sum汇总值
mean平均值
mad平均绝对偏差
median算数中位数
min最小值
max最大值
mode众数
abs绝对值
prod乘积
std贝塞尔校正的样本标准偏差
var无偏方差
sem平均值的标准误差
skew样本偏度 (第三阶)
kurt样本峰度 (第四阶)
quantile样本分位数 (不同 % 的值)
cumsum累加
cumprod累乘
cummax累积最大值
cummin累积最小值
最大/小位置索引:

Series 与 DataFrame 的 idxmax()idxmin() 函数计算最大值与最小值对应的索引。

idxminidxmax 对应 NumPy 里的 argminargmax

apply()函数:

apply()方法沿着 DataFrame 的轴应用函数,比如,描述性统计方法,该方法支持 axis 参数。

它就是对DataFrame的行或列进行操作

In [141]: df.apply(np.mean)
Out[141]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [142]: df.apply(np.mean, axis=1)
Out[142]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64

In [143]: df.apply(lambda x: x.max() - x.min())
Out[143]: 
one      1.051928
two      1.632779
three    1.840607
dtype: float64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

apply() 方法还支持通过函数名字符串调用函数。

In [146]: df.apply('mean')
Out[146]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [147]: df.apply('mean', axis=1)
Out[147]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
聚合API

就是组合多个函数

tsdf.agg(['sum', 'mean'])
Out[160]: 
             A         B         C
sum   3.033606 -1.803879  1.575510
mean  0.505601 -0.300647  0.262585

In [161]: tsdf.A.agg(['sum', 'mean'])
Out[161]: 
sum     3.033606
mean    0.505601
Name: A, dtype: float64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
用字典实现聚合

指定为哪些列应用哪些聚合函数时,需要把包含列名与标量(或标量列表)的字典传递给 DataFrame.agg

注意:这里输出结果的顺序不是固定的,要想让输出顺序与输入顺序一致,请使用 OrderedDict

In [165]: tsdf.agg({'A': 'mean', 'B': 'sum'})
Out[165]: 
A    0.505601
B   -1.803879
dtype: float64
  • 1
  • 2
  • 3
  • 4
  • 5

输入的参数是列表时,输出结果为 DataFrame,并以矩阵形式显示所有聚合函数的计算结果,且输出结果由所有唯一函数组成。未执行聚合操作的列输出结果为 NaN 值:

In [166]: tsdf.agg({'A': ['mean', 'min'], 'B': 'sum'})
Out[166]: 
             A         B
mean  0.505601       NaN
min  -0.749892       NaN
sum        NaN -1.803879
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
transform函数

.transform() 支持 NumPy 函数、字符串函数及自定义函数。

tsdf.transform(np.abs)/tsdf.transform('abs')/tsdf.transform(lambda x: x.abs())

  • 1
  • 2

函数字典可以为每列执行指定 transform() 操作。

tsdf.transform({'A': np.abs, 'B': lambda x: x + 1})

valus值可以使用列表
  • 1
  • 2
  • 3

Series

Series是一维的,显示列索引,Series(data=[],index=[])

import pandas as pd
from pandas import Series

s= Series(data=[1,2,3,'four'],index=['a','b','c','d'])
dic={
    '语文':120,
    '数学':117,
    '英语':121,
    '理综':224,
}
s= Series(data=dic)		#数据源可以是numpy的矩阵、数组也可以是字典
s[0:2]			#切片操作和numpy一样


head(),tail():首尾5个数据
unique():去重
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

s=dp.Series([1,3,6,np.nan,44,1]) #pandas会自动给列表内元素加上编号,一个一行输出

pandas就是把列表内数据进行行和列的排序,行和列的名字可以直接定义并输出

DataFrame:

DataFrame是二维的,自动生成行列的索引

from pandas import DataFrame
df=DataFrame(data=np.random.randint(10,20,size=(4,4)),index=['a','b','c','d']

dic={
    'name':['zhangsan','liis','wangwu'],
    'salary':[1000,2000,3000]
}
df=DataFrame(data=dic)

DataFrame的属性:values(返回整个数据的数组),columns(行索引),index(列索引),shape()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

dates=pd.date_range(‘20160101’,perides=6) 从1号一直输出到6号,因为给perides=6

df=pd.DataFrame(np.random.randn(6,4),index=dates,columns=[‘a’,‘b’,‘c’,‘d’])

获取属性

我们可以打印

Series: dtype, index,values,name, shape,

DataFrame: dtypes,index , columns,values,shape , .T

(类型,行索引、列索引、值,长度)

describe()/ info(): 获取数据信息

读写文件

header=None表示第一行不作为列名,index_col表示把某一列或几列作为索引,索引的内容将会在第三章进行详述,usecols表示读取列的集合,默认读取所有的列,parse_dates表示需要转化为时间的列,关于时间序列的有关内容将在第十章讲解,nrows表示读取的数据行数。上面这些参数在上述的三个函数里都可以使用。

pd.read_csv('../data/my_csv.csv', index_col=['col1', 'col2']) 可指定多列为索引列

pd.read_csv('../data/my_csv.csv', parse_dates=['col5']) 指定时间列

-----
在读取txt文件时,经常遇到分隔符非空格的情况,read_table有一个分割参数sep,它使得用户可以自定义分割符号,进行txt数据的读取。例如,下面的读取的表以||||为分割:

上面的结果显然不是理想的,这时可以使用sep,同时需要指定引擎为python:

pd.read_table('../data/my_table_special_sep.txt', sep=' \|\|\|\| ', engine='python')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
特征统计函数:

需要介绍的是quantile, count, idxmax这三个函数,它们分别返回的是分位数、非缺失值个数、最大值对应的索引:

df_demo.quantile(0.75)
df_demo.count()
df_demo.idxmax() # idxmin是对应的函数
  • 1
  • 2
  • 3

唯一值:

对序列使用uniquenunique可以分别得到其唯一值组成的列表和唯一值的个数:

df['School'].unique()

df['School'].nunique()
  • 1
  • 2
  • 3

去重的使用:

duplicateddrop_duplicates的功能类似,但前者返回了是否为唯一值的布尔列表,其keep参数与后者一致。其返回的序列,把重复元素设为True,否则为Falsedrop_duplicates等价于把duplicatedTrue的对应行剔除。

df_demo.drop_duplicates(['Name', 'Gender'], keep=False).head() # 保留只出现过一次的性别和姓名组合
  • 1

替换函数:

替换操作是针对某一个列进行的,因此下面的例子都以Series举例。pandas中的替换函数可以归纳为三类:映射替换、逻辑替换、数值替换。其中映射替换包含replace方法、第八章中的str.replace方法以及第九章中的cat.codes方法,此处介绍replace的用法。

在replace中, 可以通过字典构造,或者传入两个列表来进行替换:

df['Gender'].replace({'Female':0, 'Male':1}).head()
# 把女换成0,男换成1
df['Gender'].replace(['Female', 'Male'], [0,1]).head()
#两种都可以

  • 1
  • 2
  • 3
  • 4
  • 5

还可以用最前一个或者后一个的值进行替换:

指定method参数为ffill则为用前面一个最近的未被替换的值进行替换,bfill则使用后面最近的未被替换的值进行替换

s = pd.Series(['a', 1, 'b', 2, 1, 1, 'a'])
s.replace([1, 2], method='ffill')
s.replace([1, 2], method='bfill') #替换1,2
  • 1
  • 2
  • 3

逻辑替换:

包括了wheremask,这两个函数是完全对称的:where函数在传入条件为False的对应行进行替换,而mask在传入条件为True的对应行进行替换,当不指定替换值时,替换为缺失值。

s = pd.Series([-1, 1.2345, 100, -50])
s.where(s<0, 100)
s.mask(s<0, -50)  #符合条件的用,对应的值进行替换


  • 1
  • 2
  • 3
  • 4
  • 5
排序:

索引排序的用法和值排序完全一致,只不过元素的值在索引中,此时需要指定索引层的名字或者层号,用参数level表示。另外,需要注意的是字符串的排列顺序由字母顺序决定。

set_index:指定多列可生成多级索引的表
demo = df[['Grade', 'Name', 'Height', 'Weight']].set_index(['Grade','Name'])

df.sort_index(level['Grade','Name'],axis=0,ascending=Flase):对行进行排序

df.sort_values(by=[‘E’]):对E这一列的值进行排序
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

多列排序:

在排序中,经常遇到多列排序的问题,比如在体重相同的情况下,对身高进行排序,并且保持身高降序排列,体重升序排列:

df_demo.sort_values(['Weight','Height'],ascending=[True,False]).head()
  • 1
选择排序:
列索引:
df[‘A’] :选择输出A这一列
df[['A','B']] : 输出多列放在列表中
等价于
df.A
在df.query里面 直接使用列名 带空格用英文 的这个符号`...`
  • 1
  • 2
  • 3
  • 4
  • 5
行索引
1.字符串索引
s = pd.Series([1, 2, 3, 4, 5, 6], index=['a', 'b', 'a', 'a', 'a', 'c'])

s['a'] 和s[['a','b']]

索引切片:(注意索引不能重复)
s['c': 'b': -2]

如果索引重复:需要排序后切片
s.sort_index()['a': 'b']

2. 整数索引
s[1]  s[[1,2,3]]

如果使用整数切片,则会取出对应索引位置的值,注意这里的整数切片同Python中的切片一样不包含右端点:
	s[1:-1:2] 步长为2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

print(df[0:3],df[‘20130102’:‘20130104’] :用切片或者指定区间

loc是标签选择

loc索引器的一般形式是loc[*, *],其中第一个*代表行的选择,第二个*代表列的选择,如果省略第二个位置写作loc[*],这个*是指行的筛选。其中,*的位置一共有五类合法对象,分别是:单个元素、元素列表、元素切片、布尔列表以及函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pnXStrJb-1681776922732)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647179615440.png)]

df.loc[20130102]

df.loc[ : ,[‘A’,‘B’]:打印所有行和A和B两列

df.loc[20130102, [ ‘A’ , ‘B’] ] :输出20130102这行的A和B两列的值

【b】*为元素列表
此时,取出列表中所有元素值对应的行或列:
df_demo.loc[['Qiang Sun','Quan Zhao'], ['School','Gender']]

【c】*为切片

之前的Series使用字符串索引时提到,如果是唯一值的起点和终点字符,那么就可以使用切片,并且包含两个端点,如果不唯一则报错:

df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender']


【d】*为布尔列表

在实际的数据处理中,根据条件来筛选行是极其常见的,此处传入loc的布尔列表与DataFrame长度相同,且列表为True的位置所对应的行会被选中,False则会被剔除。

例如,选出体重超过70kg的学生:
df_demo.loc[df_demo.Weight>70].head()

也可以通过isin方法返回的布尔列表等价写出,例如选出所有大一和大四的同学信息:
df_demo.loc[df_demo.Grade.isin(['Freshman', 'Senior'])].head()

isnotin???? 不不不,这里是使用bool类型,前面加逻辑非运算就好了
df_demo.loc[~df_demo.Grade.isin(['Freshman', 'Senior'])].head()


组合条件查询:
#组合1
condition_1_1 = df_demo.School == 'Fudan University'
condition_1_2 = df_demo.Grade == 'Senior'
condition_1_3 = df_demo.Weight > 70

condition_1 = condition_1_1 & condition_1_2 & condition_1_3
#组合2
condition_2_1 = df_demo.School == 'Peking University'
condition_2_2 = df_demo.Grade == 'Senior'
condition_2_3 = df_demo.Weight > 80

condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3

df_demo.loc[condition_1 | condition_2]

将条件查询封装成函数
def condition(x):
    condition_1_1 = x.School == 'Fudan University'
    condition_1_2 = x.Grade == 'Senior'
    condition_1_3 = x.Weight > 70
    condition_1 = condition_1_1 & condition_1_2 & condition_1_3
    condition_2_1 = x.School == 'Peking University'
    condition_2_2 = x.Grade == 'Senior'
    condition_2_3 = x.Weight > 80
    condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3
    result = condition_1 | condition_2
    return result
    
df_demo.loc[condition]


lambda形式:
df_demo.loc[lambda x:'Quan Zhao', lambda x:'Gender']

由于函数无法返回如start: end: step的切片形式,故返回切片时要用slice对象进行包装:
df_demo.loc[lambda x: slice('Gaojuan You', 'Gaoqiang Qian')]
  • 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
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
iloc是位置选择

df.iloc[3,1] :选择第三行第一列

df.iloc[ [3:5 , 1:3] :三到五行的一到三列

di.iloc[[1,3,5],1:3] :选择1,3,5行的第一到第三列

布尔选择:

在使用布尔列表的时候要特别注意,不能传入Series,而必须传入序列的values,否则会报错。因此,在使用布尔筛选的时候还是应当优先考虑loc的方式。

df_demo.iloc[(df_demo.Weight>80).values].head()
  • 1

ix是标签和位置一起使用

df.ix[ :3 , [ ‘A’ , ‘C’ ]] :第0行到第3行,A和C两列

是否筛选:

print(df[df.A>8]) :在A这列筛选值大于8的,A列输出满足条件的行时,同行的数据也会输出

选定位置后赋值:

df.iloc[2,2]=1111 df.iloc[‘20100101’ , ‘B’]=2222 横向和纵向定位更改

df[df.A>0]=0 A这列大于0的数字变成0

df[‘F’]=np.nan 加上F这列,赋值nan

df[‘E’]=pd.Series( [1,2,3,4,5,6] ,index=pd.date_range(‘20130101’ , periods=6) )

缺失值统计

缺失数据可以使用isnaisnull(两个函数没有区别)来查看每个单元格是否缺失,结合mean可以计算出每列缺失值的比例:

df.isnull()/isna()
  • 1

如果想要查看某一列缺失或者非缺失的行,可以利用Series上的isna或者notna进行布尔索引。

例如,查看身高缺失的行:

df[df.Height.isna()]
  • 1

如果想要同时对几个列,检索出全部为缺失或者至少有一个缺失或者没有缺失的行,可以使用isna, notnaany, all的组合。例如,对身高、体重和转系情况这3列分别进行这三种情况的检索:

sub_set = df[['Height', 'Weight', 'Transfer']]
df[sub_set.isna().all(1)] # 全部缺失
df[sub_set.isna().any(1)].head() # 至少有一个缺失
df[sub_set.notna().all(1)].head() # 没有缺失
  • 1
  • 2
  • 3
  • 4
dropna

dropna的主要参数为轴方向axis(默认为0,即删除行)、删除方式how、删除的非缺失值个数阈值thresh(非缺失值非缺失值没有达到这个数量的相应维度会被删除)、备选的删除子集subset,其中how主要有anyall两种参数可以选择。

例如,删除身高体重至少有一个缺失的行:
res = df.dropna(how = 'any', subset = ['Height', 'Weight'])
res.shape
例如,删除超过15个缺失值的列:
res = df.dropna(1, thresh=df.shape[0]-15) # 身高被删除
res.head()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
fillna

fillna中有三个参数是常用的:value, method, limit。其中,value为填充值,可以是标量,也可以是索引到元素的字典映射;

method为填充方法,有用前面的元素填充ffill和用后面的元素填充bfill两种类型,

limit参数表示连续缺失值的最大填充次数。

s.fillna(method='ffill') # 用前面的值向后填充
s.fillna(method='ffill', limit=1) # 连续出现的缺失,最多填充一次
s.fillna(s.mean()) # value为标量
s.fillna({'a': 100, 'd': 200}) # 通过索引映射填充的值

有时为了更加合理地填充,需要先进行分组后再操作。例如,根据年级进行身高的均值填充:
df.groupby('Grade')['Height'].transform(lambda x: x.fillna(x.mean())).head()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
插值函数

线性插值、最近邻插值和索引插值

对于interpolate而言,除了插值方法(默认为linear线性插值)之外,有与fillna类似的两个常用参数,

一个是控制方向的limit_direction

另一个是控制最大连续缺失值插值个数的limit

其中,限制插值的方向默认为forward,这与fillnamethod中的ffill是类似的,若想要后向限制插值或者双向限制插值可以指定为backwardboth

默认线性插值法下分别进行backward和双向限制插值,同时限制最大连续条数为1:
res = s.interpolate(limit_direction='backward/both', limit=1)
res.values

第二种常见的插值是最近邻插补,即缺失值的元素和离它最近的非缺失值元素一样:
s.interpolate('nearest').values

最后来介绍索引插值,即根据索引大小进行线性插值。例如,构造不等间距的索引进行演示:

s.interpolate() # 默认的线性插值,等价于计算中点的值,1和10中间缺失补5
s.interpolate(method='index') 
# 和索引有关的线性插值,计算相应索引大小对应的值,1和10中间缺失补1,因为差10个索引,每个索引为1 ,即索引线性

对于时间戳亦可用

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

python中的缺失值用None表示,该元素除了等于自己本身之外,与其他任何元素不相等:

numpy中利用np.nan来表示缺失值,该元素除了不和其他任何元素相等之外,和自身的比较结果也返回False

在时间序列的对象中,pandas利用pd.NaT来指代缺失值,它的作用和np.nan是一致的

由于np.nan的浮点性质,如果在一个整数的Series中出现缺失,那么其类型会转变为float64;而如果在一个布尔类型的序列中出现缺失,那么其类型就会转为object而不是bool

Nullable类型的性质

从字面意义上看Nullable就是可空的,言下之意就是序列类型不受缺失值的影响。例如,在上述三个Nullable类型中存储缺失值,都会转为pandas内置的pd.NA

一般在实际数据处理时,可以在数据集读入后,先通过convert_dtypes转为Nullable类型:

df = pd.read_csv('../data/learn_pandas.csv')
df = df.convert_dtypes()
df.dtypes
  • 1
  • 2
  • 3

缺失数据的计算:

  1. 当调用函数sum, prod使用加法和乘法的时候,缺失数据等价于被分别视作0和1,即不改变原来的计算结果:

  2. 当使用累计函数时,会自动跳过缺失值所处的位置:

  3. 另外需要注意的是,diff, pct_change这两个函数虽然功能相似,但是对于缺失的处理不同,前者凡是参与缺失计算的部分全部设为了缺失值,而后者缺失值位置会被设为 0% 的变化率

删除行列:
#删除B和C两列,两种方法
df.drop(['B', 'C'], axis=1)
df.drop(columns=['B', 'C'])

#删除行
df.drop([0])
df.drop([0, 1])

#删除(del、pop)列的方式也与字典类似
del df['two']

three = df.pop('three')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
query方法

pandas中,支持把字符串形式的查询表达式传入query方法来查询数据,其表达式的执行结果必须返回布尔列表。在进行复杂索引时,由于这种检索方式无需像普通方法一样重复使用DataFrame的名字来引用列名,一般而言会使代码长度在不降低可读性的前提下有所减少。

df.query('((School == "Fudan University")&'
         ' (Grade == "Senior")&'
         ' (Weight > 70))|'
         '((School == "Peking University")&'
         ' (Grade != "Senior")&'
         ' (Weight > 80))')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

query表达式中,帮用户注册了所有来自DataFrame的列名,所有属于该Series的方法都可以被调用,和正常的函数调用并没有区别,例如查询体重超过均值的学生:

df.query('Weight > Weight.mean()').head() 
  • 1

对于含有空格的列名,需要使用英文顿号col name的方式进行引用

同时,在query中还注册了若干英语的字面用法,帮助提高可读性,例如:or, and, or, in, not in。例如,筛选出男生中不是大一大二的学生:

df.query('(Grade not in ["Freshman", "Sophomore"]) and (Gender == "Male")').head()
  • 1

此外,在字符串中出现与列表的比较时,==!=分别表示元素出现在列表和没有出现在列表,等价于innot in,例如查询所有大三和大四的学生:

df.query('Grade == ["Junior", "Senior"]').head()
  • 1

引入外部变量:

对于query中的字符串,如果要引用外部变量,只需在变量名前加@符号。例如,取出体重位于70kg到80kg之间的学生:

low, high =70, 80
df.query('Weight.between(@low, @high)').head()
  • 1
  • 2
索引案例
df['Cocoa Percent'] = df['Cocoa Percent'].apply(lambda x:float(x[:-1])/100)
df.query('(Rating<3)&(`Cocoa Percent`>`Cocoa Percent`.median())')

df.loc[lambda x:x.Rating<=2.75&(x["Cocoa Percent"]>x["Cocoa Percent"].median())]
idx = pd.IndexSlice
exclude = ['France', 'Canada', 'Amsterdam', 'Belgium']
res = df.set_index(['Review Date', 'Company Location']).sort_index(level=0)

#这里对索引再就行筛选
res.loc[idx[2012:,~res.index.get_level_values(1).isin(exclude)],:].head(3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
随机抽样:

如果把DataFrame的每一行看作一个样本,或把每一列看作一个特征,再把整个DataFrame看作总体,想要对样本或特征进行随机抽样就可以用sample函数。有时在拿到大型数据集后,想要对统计特征进行计算来了解数据的大致分布,但是这很费时间。同时,由于许多统计特征在等概率不放回的简单随机抽样条件下,是总体统计特征的无偏估计,比如样本均值和总体均值,那么就可以先从整张表中抽出一部分来做近似估计


sample函数中的主要参数为n, axis, frac, replace, weights,前三个分别是指抽样数量、抽样的方向(0为行、1为列)和抽样比例(0.3则为从总体中抽出30%的样本)。

replaceweights分别是指是否放回和每个样本的抽样相对概率,当replace = True则表示有放回抽样。例如,对下面构造的df_samplevalue值的相对大小为抽样概率进行有放回抽样,抽样数量为3。

df_sample.sample(3, replace = True, weights = df_sample.value)
  • 1
多级索引

loc:

由于多级索引中的单个元素以元组为单位,因此之前在第一节介绍的 lociloc 方法完全可以照搬,只需把标量的位置替换成对应的元组。

当传入元组列表或单个元组或返回前二者的函数时,需要先进行索引排序以避免性能警告:

df_sorted = df_multi.sort_index()
df_sorted.loc[('Fudan University', 'Junior')].head()
  • 1
  • 2
获取索引:
df.index/columns.names/values
df.index.get_level_values(0/1) 获取第一层、第二层

但对于索引而言,无论是单层还是多层,用户都无法通过index_obj[0] = item的方式来修改元素,也不能通过index_name[0] = new_name的方式来修改名字

经典案例:
idx = pd.IndexSlice
exclude = ['France', 'Canada', 'Amsterdam', 'Belgium']
res = df.set_index(['Review Date', 'Company Location']).sort_index(level=0)

res.loc[idx[2012:,~res.index.get_level_values(1).isin(exclude)],:].head(3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

当使用切片时需要注意,在单级索引中只要切片端点元素是唯一的,那么就可以进行切片,但在多级索引中,无论元组在索引中是否重复出现,都必须经过排序才能使用切片,否则报错

#报错
df_multi.loc[('Fudan University', 'Senior'):].head()
df_unique.loc[('Fudan University', 'Senior'):].head()
#更正
df_unique.sort_index().loc[('Fudan University', 'Senior'):].head()
  • 1
  • 2
  • 3
  • 4
  • 5

在多级索引中的元组有一种特殊的用法,可以对多层的元素进行交叉组合后索引,但同时需要指定loc的列,全选则用:表示。其中,每一层需要选中的元素用列表存放,传入loc的形式为[(level_0_list, level_1_list), cols]。例如,想要得到所有北大和复旦的大二大三学生,可以如下写出:

res = df_multi.loc[(['Peking University', 'Fudan University'], ['Sophomore', 'Junior']), :]
res.head()
  • 1
  • 2

下面的语句和上面类似,但仍然传入的是元素(这里为元组)的列表,它们的意义是不同的,表示的是选出北大的大三学生和复旦的大二学生:

res = df_multi.loc[[('Peking University', 'Junior'), ('Fudan University', 'Sophomore')]]
res.head()
  • 1
  • 2

想获取查询的子表的长度时,别用len了!!

result.shape[0] / shape[1]
  • 1
indexSlice对象

前面介绍的方法,即使在索引不重复的时候,也只能对元组整体进行切片,而不能对每层进行切片,也不允许将切片和布尔列表混合使用,引入IndexSlice对象就能解决这个问题。Slice对象一共有两种形式,第一种为loc[idx[*,*]]型,第二种为loc[idx[*,*],idx[*,*]]

为了使用silce对象,先要进行定义:

idx = pd.IndexSlice
loc[idx[*,*]]型

这种情况并不能进行多层分别切片,前一个*表示行的选择,后一个*表示列的选择,与单纯的loc是类似的:

df_ex.loc[idx['C':, ('D', 'f'):]]
df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0

------

loc[idx[*,*],idx[*,*]]型

这种情况能够分层进行切片,前一个idx指代的是行索引,后一个是列索引。
df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]]
但需要注意的是,此时不支持使用函数
例如:
df_ex.loc[idx[:'A', lambda x: 'b'], idx['E':, 'e':]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
多级索引构造

前面提到了多级索引表的结构和切片,那么除了使用set_index之外,如何自己构造多级索引呢?常用的有from_tuples, from_arrays, from_product三种方法,它们都是pd.MultiIndex对象下的函数。

from_tuples指根据传入由元组组成的列表进行构造:

my_tuple = [('a','cat'),('a','dog'),('b','cat'),('b','dog')]
pd.MultiIndex.from_tuples(my_tuple, names=['First','Second'])
  • 1
  • 2

from_arrays指根据传入列表中,对应层的列表进行构造:

my_array = [list('aabb'), ['cat', 'dog']*2]
pd.MultiIndex.from_arrays(my_array, names=['First','Second'])
  • 1
  • 2

from_product指根据给定多个列表的笛卡尔积进行构造:

my_list1 = ['a','b']
my_list2 = ['cat','dog']
pd.MultiIndex.from_product([my_list1, my_list2], names=['First','Second'])
  • 1
  • 2
  • 3

构造举例:

p.random.seed(0)
L1,L2,L3 = ['A','B'],['a','b'],['alpha','beta']
mul_index1 = pd.MultiIndex.from_product([L1,L2,L3], names=('Upper', 'Lower','Extra'))
L4,L5,L6 = ['C','D'],['c','d'],['cat','dog']
mul_index2 = pd.MultiIndex.from_product([L4,L5,L6], names=('Big', 'Small', 'Other'))
df_ex = pd.DataFrame(np.random.randint(-9,10,(8,8)), index=mul_index1,  columns=mul_index2)
df_ex
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-riUavTPe-1681776922733)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647218897760.png)]

交换:

索引层的交换由swaplevelreorder_levels完成,前者只能交换两个层,而后者可以交换任意层,两者都可以指定交换的是轴是哪一个,即行索引或列索引:

df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一层和第三层交换

df_ex.reorder_levels([2,0,1],axis=0).head() 
# 列表数字指代原来索引中的层
把原来第3层换到第1层,原来第1层换到第2层,原来第2层换到第3层
  • 1
  • 2
  • 3
  • 4
  • 5

删除某一层:

df_ex.droplevel([0,1],axis=0/1)  #0是行索引,1是列索引
  • 1

修改:

通过rename_axis可以对索引层的名字进行修改,常用的修改方式是传入字典的映射:

df_ex.rename_axis(index={'Upper':'Changed_row'}, columns={'Other':'Changed_Col'}).head()

通过rename可以对索引的值进行修改,如果是多级索引需要指定修改的层号level:
df_ex.rename(columns={'cat':'not_cat'}, level=2).head()

df_ex.rename(index=lambda x:str.upper(x), level=2).head()

另外一个需要介绍的函数是map,它是定义在Index上的方法,与前面rename方法中层的函数式用法是类似的,只不过它传入的不是层的标量值,而是直接传入索引的元组,这为用户进行跨层的修改提供了遍历
df_temp = df_ex.copy()
new_idx = df_temp.index.map(
lambda x: (x[0], x[1], str.upper(x[2])))
df_temp.index = new_idx
df_temp.head()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
索引的设置与重置

索引的设置可以使用set_index完成,这里的主要参数是append,表示是否来保留原来的索引,直接把新设定的添加到原索引的内层:

df_new.set_index(['A','B'])
  • 1

reset_indexset_index的逆函数,其主要参数是drop,表示是否要把去掉的索引层丢弃,而不是添加到列中:

df_new.reset_index(['D'],drop=False)
  • 1
更改行列标签名字:
1.df.columns 查看当前行标签
  	df.indexs  = []
	df.columns = []新标签列表
2.DataFrame.rename(index={},columns={'原标签名':'新标签名'})
	df.rename(columns={"A":"a"})
	df.rename(index={0:'A'})
3.df.reindex(index=[...],columns=[...])	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

多重索引:

df = pd.DataFrame({'x': [1, 2, 3, 4, 5, 6],
             'y': [10, 20, 30, 40, 50, 60]},
             index=pd.MultiIndex.from_product([['a', 'b', 'c'], [1, 2]],
             names=['let', 'num']))
             
In [242]: df
Out[242]: 
         x   y
let num       
a   1    1  10
    2    2  20
b   1    3  30
    2    4  40
c   1    5  50
    2    6  60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
数据删除和填充:

NAN可以参与运算,None不能参与运算

all:用来检测行或者列中是否存在True

df.isnull().any(axis=1)

df.dropna(axis=0,how=‘any’)

在行中有任何一个none就就掉,如果how=all的话,一行所有的都是none就丢掉

df.fillna(value=填入值) 把已有的none自动填入数据

nf.fillna(method=‘ffill/bfill’,axis=0/1),向前/后填充,水平/上下

df.isnull() 缺失返回True,否则Flase,永远结合any,只要有True就返回True

df.notnull():缺失返回Flase,否则True,永远结合all,只要有True就返回True

读表格:pd.read_excel/csv(‘文件名+后缀’)

改行索引:

df.set_index(‘date’,inplace=True)

删除重复:

nf.drop_duplicates(keep = ‘first/last’)

级联

pd.concat(df1,df2,axis=1/0)

合并:
pd.merge(df1,df2,on='根据哪一列合并,即合并条件,合并的列都要有是共同的')

merge(df1,df2,how='inner/outner/right/left')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
.dt 访问器

Series 提供一个可以简单、快捷地返回 datetime 属性值的访问器。这个访问器返回的也是 Series,索引与现有的 Series 一样。

s = pd.Series(pd.date_range('20130101 09:10:12', periods=4))
s.dt.hour
s.dt.second
s.dt.day

筛选:
s[s.dt.day == 2]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
日期处理

还可以用 [Series.dt.strftime()datetime 的值当成字符串进行格式化,支持与标准 [strftime() 同样的格式。

s.dt.strftime('%Y/%m/%d')

df_dt.apply(lambda x: datetime.strftime(x, format))
或
df_dt.dt.strftime(format)

#将int转换成str
df_date = df['日期'].apply(str)
#用to_datetime()函数将字符串转换成时间格式,并增加'时间'字段
df['时间'] = pd.to_datetime(df_date,format='%Y/%m/%d')
print(df['时间'])
#将日期格式化,并增加'格式化日期'字段
df['格式化日期1'] = df.时间.apply(lambda x: datetime.
                               strftime(x, format='%Y-%m-%d'))
df['格式化日期2'] = df.时间.dt.strftime('%Y-%m-%d')
print(df['格式化日期1'],'\n',df['格式化日期2'])
#抽取'时间'字段中的值
df['时间.年'] = df['时间'].dt.year
df['时间.月'] = df['时间'].dt.month
df['时间.周'] = df['时间'].dt.weekday
df['时间.日'] = df['时间'].dt.day
df['时间.时'] = df['时间'].dt.hour
df['时间.分'] = df['时间'].dt.minute
df['时间.秒'] = df['时间'].dt.second
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
修改指定类型:
#法一:
dft = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6], 'c': [7, 8, 9]})

dft[['a', 'b']] = dft[['a', 'b']].astype(np.uint8)

#法二:
dft1 = dft1.astype({'a': np.bool, 'c': np.float64})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Pandas 提供了多种函数可以把 object 从一种类型强制转为另一种类型。这是因为,数据有时存储的是正确类型,但在保存时却存成了 object 类型,此时,用 DataFrame.infer_objects()Series.infer_objects() 方法即可把数据转换为正确的类型。

df.infer_objects().dtypes
  • 1

下列函数可以应用于一维数组与标量,执行硬转换,把对象转换为指定类型。

  • to_numeric() ,转换为数值型
In [370]: m = ['1.1', 2, 3]

In [371]: pd.to_numeric(m)
Out[371]: array([1.1, 2. , 3. ])
  • 1
  • 2
  • 3
  • 4
In [372]: import datetime

In [373]: m = ['2016-07-09', datetime.datetime(2016, 3, 2)]

In [374]: pd.to_datetime(m)
Out[374]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
分组(Grouping)

groupby后会生成一个groupby对象,该对象不会返回任何内容

从上述的几个例子中不难看出,想要实现分组操作,必须明确三个要素:

分组依据、数据来源、操作及其返回结果。

同时从充分性的角度来说,如果明确了这三方面,就能确定一个分组操作,从而分组代码的一般模式即:

df.groupby(分组依据)[数据来源].使用操作
  • 1

例如第一个例子中的代码就应该如下:

df.groupby('Gender')['Longevity'].mean()
  • 1

基本内容:

  1. 根据某一列分组
  2. 根据某几列分组
  3. 组容量与组数
  4. 组的遍历
  5. level参数(多级索引)和axis参数

分组完想df可视化 : to_frame()

“group by” 指的是涵盖下列一项或多项步骤的处理流程:

  • 分割:按条件把数据分割成多组;
  • 应用:为每组单独应用函数;
  • 组合:将处理结果组合成一个数据结构
分组后取出一个分组:
df_group = df.groupby('...')
df_group.get_group(分组名)

根据几组分:df.groupby(['...'])

查看组的容量: group.size  返回的是表长乘以表宽的大小,但在groupby对象上表示统计每个组的元素个数

查看组数:group.ngroups

groups属性是获取分组的字典:gb.groups.item()/keys()

分完组然后选中列:df.groupby('...')[['col1,col2']].mean()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

条件分组:

condition = df.Weight > df.Weight.mean()
df.groupby(condition)['Height'].mean()

根据上下四分位数分割,将体重分为high、normal、low三组,统计身高的均值
def split_weight(x):
    if x>df.Weight.quantile(0.75):
        return 'high'
    elif x<df.Weight.quantile(0.25):
        return 'low'
    else:
        return 'normal'
df.groupby(split_weight)['Height'].mean()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
 df = pd.DataFrame(
 {'A': ['foo', 'bar', 'foo', 'bar',
                      'foo', 'bar', 'foo', 'foo'],
 'B': ['one', 'one', 'two', 'three',                          'two', 'two', 'one', 'three'],
 'C': np.random.randn(8),
 'D': np.random.randn(8)})
         		      
df.groupby('A').sum()
df.groupby(['A', 'B']).sum()

通过get_group方法可以直接获取所在组对应的行,此时必须知道组的具体名字:
gb.get_group(('Fudan University', 'Freshman'))

连续变量分组:
bins = [0,40,60,80,100]
cuts = pd.cut(df['Math'],bins=bin)
df.groupby(cuts)['Math'].count()

同时使用多个聚合函数:
group_m.agg('sum','mean','std')

group_m.agg({'Math':['sum','mean','std'],'Height':'var'})

group_m['Math'].agg(lambda x: x.max()-min())


  • 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

1. 内置聚合函数

在介绍agg之前,首先要了解一些直接定义在groupby对象的聚合函数,因为它的速度基本都会经过内部的优化,使用功能时应当优先考虑。根据返回标量值的原则,包括如下函数:max/min/mean/median/count/all/any/idxmax/idxmin/mad/nunique/skew/quantile/sum/std/var/sem/size/prod

这些聚合函数当传入的数据来源包含多个列时,将按照列进行迭代计算:

gb = df.groupby('Gender')[['Height', 'Weight']]
gb.max()
  • 1
  • 2
  • 3
  • 4
agg方法

虽然在groupby对象上定义了许多方便的函数,但仍然有以下不便之处:

  • 无法同时使用多个函数
  • 无法对特定的列使用特定的聚合函数
  • 无法使用自定义的聚合函数
  • 无法直接对结果的列名在聚合前进行自定义命名

问题解决方法:

【a】使用多个函数

当使用多个聚合函数时,需要用列表的形式把内置聚合函数对应的字符串传入,先前提到的所有字符串都是合法的。
gb.agg(['sum', 'idxmax', 'skew'])

【b】对特定的列使用特定的聚合函数
对于方法和列的特殊对应,可以通过构造字典传入agg中实现,其中字典以列名为键,以聚合字符串或字符串列表为值。
gb.agg({'Height':['mean','max'], 'Weight':'count'})

【c】使用自定义函数

在agg中可以使用具体的自定义函数, 需要注意传入函数的参数是之前数据源中的列,逐列进行计算 。
gb.agg(lambda x: x.mean()-x.min())

由于传入的是序列,因此序列上的方法和属性都是可以在函数中使用的,只需保证返回值是标量即可。
下面的例子是指,如果组的指标均值,超过该指标的总体均值,返回High,否则返回Low。

def my_func(s):
    res = 'High'
    if s.mean() <= df[s.name].mean():
        res = 'Low'
    return res
gb.agg(my_func)

【d】聚合结果重命名

如果想要对聚合结果的列名进行重命名,只需要将上述函数的位置改写成元组,元组的第一个元素为新的名字,第二个位置为原来的函数,包括聚合字符串和自定义函数,现举若干例子说明:
gb.agg([('range', lambda x: x.max()-x.min()), ('my_sum', 'sum')])

gb.agg({'Height': [('my_func', my_func), 'sum'], 'Weight': lambda x:x.max()})

另外需要注意,使用对一个或者多个列使用单个聚合的时候,重命名需要加方括号,否则就不知道是新的名字还是手误输错的内置函数字符串:
gb.agg([('my_sum', 'sum')])
  • 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
  • 31
  • 32
  • 33
三、变换和过滤

变换函数的返回值为同长度的序列,最常用的内置变换函数是累计函数:cumcount/cumsum/cumprod/cummax/cummin,它们的使用方式和聚合函数类似,只不过完成的是组内累计操作。

当用自定义变换时需要使用transform方法,被调用的自定义函数,其传入值为数据源的序列其传入值为数据源的序列,与agg的传入类型是一致的,其最后的返回结果是行列索引与数据源一致的DataFrame

gb.transform(lambda x: (x-x.mean())/x.std()).head()
  • 1

组过滤作为行过滤的推广,指的是如果对一个组的全体所在行进行统计的结果返回True则会被保留,False则该组会被过滤,最后把所有未被过滤的组其对应的所在行拼接起来作为DataFrame返回。

groupby对象中,定义了filter方法进行组的筛选,其中自定义函数的输入参数为数据源构成的DataFrame本身,在之前例子中定义的groupby对象中,传入的就是df[['Height', 'Weight']],因此所有表方法和属性都可以在自定义函数中相应地使用,同时只需保证自定义函数的返回为布尔值即可。

在原表中通过过滤得到所有容量大于100的组:
gb.filter(lambda x: x.shape[0] > 100).head()
  • 1
  • 2

分组apply用法:

def BMI(x):
    Height = x['Height']/100
    Weight = x['Weight']
    BMI_value = Weight/Height**2
    return BMI_value.mean()
gb.apply(BMI)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
分组案例实战
先过滤出所属Country数超过2个的汽车,即若该汽车的Country在总体数据集中出现次数不超过2则剔除,再按Country分组计算价格均值、价格变异系数、该Country的汽车数量,其中变异系数的计算方法是标准差除以均值,并在结果中把变异系数重命名为CoV。
按照表中位置的前三分之一、中间三分之一和后三分之一分组,统计Price的均值。
对类型Type分组,对Price和HP分别计算最大值和最小值,结果会产生多级索引,请用下划线把多级列索引合并为单层索引。
对类型Type分组,对HP进行组内的min-max归一化。
对类型Type分组,计算Disp.与HP的相关系数。

df.groupby('Country').filter(lambda x:x.shape[0]>2)
.groupby('Country')['Price']
.agg([('CoV', lambda x: x.std()/x.mean()), 'mean', 'count'])

#表的长度为60
condition = ['Head']*20+['Mid']*20+['Tail']*20

df.groupby(condition)['Price'].mean()

res = df.groupby('Type').agg({'Price': ['max'], 'HP': ['min']})
# res.columns = res.columns.map(lambda x:'_'.join(x))
res.columns

res = df.groupby('Type')['Price','HP'].agg(['max','min'])
# res.columns = res,columns.map(lambda x:'_'.join(x))
res


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Apply函数用法

apply函数会遍历指定的所有行或者列

  1. 标量返回

    df[['school','Math','Height']].groupby('school').apply(lambda x:x.max())
    
    • 1
  2. 列表返回

    df[['school','Math','Height']].groupby('school')
    .apply(lambda x: pd.DataFrame({"col":values}))
    
    • 1
    • 2
  3. 数据框返回

criterion = ver['applicant_race_name_1'].map(lambda x: x.startswith('W'))

df2['..'] = df2['...'].map(str.strip)
df2.columns = df2.columns.str.upper
df['...'].apply(str.upper)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

自定义函数:

自定义函数的接收值就是前面指定的行/列的值,经过处理后,return的值就是填充进去的

def my_mean(x):
     res = x.mean()
     return res
df_demo.apply(my_mean)
  • 1
  • 2
  • 3
  • 4

窗口对象

pandas中有3类窗口,分别是滑动窗口rolling、扩张窗口expanding以及指数加权窗口ewm。需要说明的是,以日期偏置为窗口大小的滑动窗口将在第十章讨论,指数加权窗口见本章练习。

1. 滑窗对象

要使用滑窗函数,就必须先要对一个序列使用.rolling得到滑窗对象,其最重要的参数为窗口大小window

s = pd.Series([1,2,3,4,5])
roller = s.rolling(window = 3)  #三个数据为一组的滑窗
roller

在得到了滑窗对象后,能够使用相应的聚合函数进行计算,需要注意的是窗口包含当前行所在的元素,例如在第四个位置进行均值运算时,应当计算(2+3+4)/3,而不是(1+2+3)/3:
roller.mean()
roller.sum()  #前面几个和后面几个会出现NAN值

滑动系数和协方差矩阵
roller.cov(s2)
roller.corr(s2)

支持传入自定义函数:
roller.apply(lambda x:x.mean())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

滑窗函数:

shift, diff, pct_change是一组类滑窗函数,它们的公共参数为periods=n,默认为1

shift表示取向前第n个元素的值

diff与向前第n个元素做差(与Numpy中不同,后者表示n阶差分)

pct_change与向前第n个元素相比计算增长率。这里的n可以为负,表示反方向的类似操作。

s = pd.Series([1,3,6,10,15])
s.shift(2)
s.diff(3)

可用apply函数等价替换
s.rolling(3).apply(lambda x:list(x)[0]) # s.shift(2)
s.rolling(4).apply(lambda x:list(x)[-1]-list(x)[0]) # s.diff(3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

扩张窗口:

扩张窗口又称累计窗口,可以理解为一个动态长度的窗口,其窗口的大小就是从序列开始处到具体操作的对应位置,其使用的聚合函数会作用于这些逐步扩张的窗口上。具体地说,设序列为a1, a2, a3, a4,则其每个位置对应的窗口即[a1]、[a1, a2]、[a1, a2, a3]、[a1, a2, a3, a4]。

s = pd.Series([1, 3, 6, 10])
s.expanding().mean()
  • 1
  • 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j2OPrmxR-1681776922734)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647178551663.png)]

Ex2:指数加权窗口
  1. 作为扩张窗口的ewm窗口

在扩张窗口中,用户可以使用各类函数进行历史的累计指标统计,但这些内置的统计函数往往把窗口中的所有元素赋予了同样的权重。事实上,可以给出不同的权重来赋给窗口中的元素,指数加权窗口就是这样一种特殊的扩张窗口。

其中,最重要的参数是alpha,它决定了默认情况下的窗口权重为

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/903147
推荐阅读
相关标签