左手用R右手Python系列之——数据框与apply向量运算

时间:2022-05-08
本文章向大家介绍左手用R右手Python系列之——数据框与apply向量运算,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

R语言与Python中的apply函数都有着丰富的应用场景,恰到好处的使用apply函数,可以避免在很多场景下书写冗余的代码,这不仅能提高代码可读性,而且提高代码执行的效率。

apply(X, MARGIN, FUN, ...)
X        #一个数组(包括矩阵)
MARGIN   #一个给定下标的向量,将被指定函数执行计算1代表行,2代表列,c(1,2)代表行列。
FUN      #执行计算的函数(如果是+、%*%这种符号函数需要使用反引号包括【英文输入法状态下的“~”键】)
...      #函数的参数

apply函数内部执行的数组运算,我们通常传入的data.frame会被强制转换为二维数组参与最终的计算。

FUN参数支持R语言中常用的基础统计函数(meanmaxminmedianmodestdvar等),或者是一些符号函数【`+`、`%*%`等,】以及通过代码创建的自定义函数。

为了加深理解,我使用一个三维数组进行演示:

x <- array(1:48,c(4,3,4))

, , 1

     [,1] [,2] [,3]
[1,]    1    5    9

[2,]    2    6   10

[3,]    3    7   11

[4,]    4    8   12

, , 2

     [,1] [,2] [,3]
[1,]   13   17   21

[2,]   14   18   22

[3,]   15   19   23

[4,]   16   20   24

, , 3

     [,1] [,2] [,3]
[1,]   25   29   33

[2,]   26   30   34

[3,]   27   31   35

[4,]   28   32   36

, , 4

     [,1] [,2] [,3]
[1,]   37   41   45

[2,]   38   42   46

[3,]   39   43   47

[4,]   40   44   48

使用apply函数对x这个三维数组进行各剖面的统计计算:

apply(x,3,mean) 
#计算第三维度(四个子矩阵块儿)矩阵均值(也可以是最大值、最小值等统计量)
[1]  6.5 18.5 30.5 42.5

因为每一个子矩阵块儿均值是单值,所有第三维度一共四个子块,返回一个单向量,依次为第三维度四个矩阵的均值量。

apply(x,c(1,3),mean) 
#第三维与行切块交叉均值

     [,1] [,2] [,3] [,4]
[1,]    5   17   29   41

[2,]    6   18   30   42

[3,]    7   19   31   43

[4,]    8   20   32   44

这是一个二维切块交叉均值统计量的输出,结果是一个矩阵,如何解读这个矩阵,其实很简单。c(1,3)实现了按照行与第三维度交叉切块,将原始三维数据组切成了4*4=16个子块【每个矩阵有四行,第三维一共有四个矩阵】。16个子块计算出mean之后,按照行列(第三维度)顺序输出矩阵。 所以以上结果的[1,1]=5是第一个矩阵第一行的均值,相当于 mean(x[1,,1]) [1] 5 依次类推,行上分别是mean(x[1,,2])mean(x[1,,3])mean(x[1,,4])的均值,列上分别是mean(x[1,,1])、mean(x[2,,1])、mean(x[3,,1])、mean(x[4,,1]) ,其他每一行或者列都可以类比推导。输出的行严格按照原始数组行顺序,输出的列严格按照原始高维数据第三维顺序。

apply(x,c(3,1),mean)

再来看一下当将MARGIN参数的向量顺序反转之后出现的情况:

    [,1] [,2] [,3] [,4]
[1,]    5    6    7    8

[2,]   17   18   19   20

[3,]   29   30   31   32

[4,]   41   42   43   44

仍然时一个向量,不过这些的输出次序变了,相当于把上一个输出转置了,实际是因为输出结果排布,第三维度变成了结果矩阵的行,行维度变成了输出结果矩阵的列。

而一共16个行的矩阵及其计算结果并没有改变。

也许这么说很快能把你绕晕了,那就来个图吧~

apply(x,c(2,3),mean)

     [,1] [,2] [,3] [,4]
[1,]  2.5 14.5 26.5 38.5

[2,]  6.5 18.5 30.5 42.5

[3,] 10.5 22.5 34.5 46.5

apply(x,c(3,2),mean)

     [,1] [,2] [,3]
[1,]  2.5  6.5 10.5

[2,] 14.5 18.5 22.5

[3,] 26.5 30.5 34.5

[4,] 38.5 42.5 46.5

列与第三维交叉均值,同样可以类比理解。

apply(x,c(1,2),mean)
     [,1] [,2] [,3]
[1,]   19   23   27

[2,]   20   24   28

[3,]   21   25   29

[4,]   22   26   30

apply(x,c(2,1),mean)
     [,1] [,2] [,3] [,4]
[1,]   19   20   21   22

[2,]   23   24   25   26

[3,]   27   28   29   30

以上是行列交叉均值,这个不太好理解,其实就是每一个第三维度行列交叉指组成的向量的均值,因为原始高维数据的每一个矩阵都是四行三列,所以最终的输出也是四行三列。

输出结果的解读可以这么来看:

mean(x[1,1,])=19

mean(x[2,1,])=20

mean(x[3,1,])=21

mean(x[4,1,])=22

其他的以此类推,apply(x,c(2,1),mean)的输出结果相当于apply(x,c(1,2),mean)的转置,其实就是颠倒了行列,计算输出时按照列顺序排布。

apply(x,c(1,2,3),mean)
, , 1

     [,1] [,2] [,3]
[1,]    1    5    9

[2,]    2    6   10

[3,]    3    7   11

[4,]    4    8   12

, , 2

     [,1] [,2] [,3]
[1,]   13   17   21

[2,]   14   18   22

[3,]   15   19   23

[4,]   16   20   24

, , 3

     [,1] [,2] [,3]
[1,]   25   29   33

[2,]   26   30   34

[3,]   27   31   35

[4,]   28   32   36

, , 4

     [,1] [,2] [,3]
[1,]   37   41   45

[2,]   38   42   46

[3,]   39   43   47

[4,]   40   44   48

使用apply(x,c(1,2,3),mean)操作,结果输出与原始三维数组x一模一样,这个也不难理解,相当于对原始数组中的单个值进行了逐次遍历,每一个单值的mean必然等于它本身。 掌握apply函数的核心思想,必须首先掌握高维数组的dim概念,array的索引概念如果熟悉,那么apply仅仅是针对每一次索引运用了统计量而已。

以上是高维数组的apply参数详解,实际上我们平时很少使用超过二维(也就是矩阵)的运算,更多的时候是使用数据框参与计算,apply计算数据框的相关变量,仅需掌握MARGIN的参数含义即可,要牢记1代表计算行,2代表计算列。

mydf <- data.frame(matrix(rnorm(48,10,5),nrow=6,dimnames=list(NULL,LETTERS[1:8])))

          A         B         C          D        E         F         G         H
1 12.613699  5.471280 10.723466  3.7370156 2.845806  6.887259  4.680479 13.422446
2 14.588237 11.027817 12.484466 -0.9092612 9.835008 17.976195 17.649579  9.651938
3  2.939922  5.989195  4.310257  6.9660366 5.623445 12.950469 10.591221  8.729425
4  4.131217 13.610730 14.042249  9.0431193 9.941429  3.802639 10.037765 11.986332
5 10.155239 11.301701  3.349171 13.3299717 1.267965 13.767660  5.857686  7.801586
6 13.993980  7.249232  6.482925  8.7703328 5.489774 14.429364  9.675892  2.890610

apply(mydf,1,mean)  #计算数据框的行均值

[1]  7.547681 11.537997  7.262496  9.574435  8.353872  8.622764

apply(mydf,2,mean)  #计算数据框的列均值
        A         B         C         D         E         F         G         H 
 9.737049  9.108326  8.565422  6.822869  5.833905 11.635598  9.748770  9.080390

除此之外,还有几个计算行列统计量的快捷函数:colSums、colMeans

colSums(mydf)
       A        B        C        D        E        F        G        H 
58.42229 54.64995 51.39253 40.93721 35.00343 69.81359 58.49262 54.48234 
rowSums(mydf)
[1] 60.38145 92.30398 58.09997 76.59548 66.83098 68.98211

colMeans(mydf)
        A         B         C         D         E         F         G         H 
 9.737049  9.108326  8.565422  6.822869  5.833905 11.635598  9.748770  9.080390 
rowMeans(mydf)
[1]  7.547681 11.537997  7.262496  9.574435  8.353872  8.622764

Python

python中涉及维度操作的函数有这么几个:applyapplymapmap

import pandas as pd
import numpy as np
df= pd.DataFrame(np.random.randn(8,6), columns=list('ABCDEF'))
?df.apply
Signature: df.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)
Docstring:
Applies function along input axis of DataFrame.

Parameters
----------
func : function
    Function to apply to each column/row
axis : {0 or 'index', 1 or 'columns'}, default 0
    * 0 or 'index': apply function to each column
    * 1 or 'columns': apply function to each row

pd中的apply方法与R语言中的apply函数用法以及参数基本一致。其中较为重要的参数是funcaxisargs

func    #参数指定需要执行的函数名称;
axis    #指定针对对象的哪个轴执行运算
args    #是func函数的可选参数

axis轴的选择规则是0或者index代表行(默认为0),1或者columns代表列。(因为Python中索引以0开始,总体顺序与R中1代表行,2代表列一致)。

df.apply(np.sum,axis=0)
Out[6]: 
A    2.042850

B   -0.018453

C   -2.656881

D   -0.621382

E   -6.504787

F   -4.363466

dtype: float64

df.apply(np.sum,axis=1)

Out[7]: 
0   -2.6334631   -3.4064622   -4.2373143    0.0073824   -2.8301175   -2.7321456    0.3313507    3.378650

dtype: float64

func参数同样支持自定义函数以及匿名函数;

df.apply(lambda x: np.sqrt(np.abs(x)),axis=1)

          A         B         C         D         E         F
0  0.518005  1.315212  0.495409  1.584483  0.134769  1.265769
1  1.016121  0.506741  1.080625  0.239525  1.509077  1.092389
2  0.812656  0.563715  0.404424  0.792446  1.685342  0.213683
3  0.191120  0.855446  0.991484  0.869212  0.683357  0.754288
4  0.649375  0.857485  0.785169  1.158319  0.751475  1.210032
5  0.187696  0.535870  1.006190  0.843008  1.148970  1.196930
6  1.170808  0.605734  0.835844  0.984158  1.005559  0.815561
7  0.679565  1.055820  0.250509  1.217166  1.412106  0.828901

applymap函数针对所有元素执行函数,不区分行列:

Signature: df.applymap(func)
Docstring:
Apply a function to a DataFrame that is intended to operate
elementwise, i.e. like doing map(func, series) for each series in the
DataFrame

Parameters
----------
func : function
    Python function, returns a single value from a single value
df.applymap(lambda x: '%.2f' % x)

       A      B      C      D      E      F
0   0.27  -1.73  -0.25  -2.51  -0.02   1.60
1   1.03   0.26  -1.17  -0.06  -2.28  -1.19
2  -0.66  -0.32   0.16  -0.63  -2.84   0.05
3   0.04  -0.73   0.98   0.76  -0.47  -0.57
4   0.42   0.74  -0.62  -1.34  -0.56  -1.46
5   0.04   0.29  -1.01   0.71  -1.32  -1.43
6   1.37   0.37  -0.70   0.97  -1.01  -0.67
7  -0.46   1.11  -0.06   1.48   1.99  -0.69
df.applymap(lambda x: x if x>0 else 0)

          A         B         C         D         E         F
0  0.268329  0.000000  0.000000  0.000000  0.000000  1.602172
1  1.032502  0.256786  0.000000  0.000000  0.000000  0.000000
2  0.000000  0.000000  0.163559  0.000000  0.000000  0.045660
3  0.036527  0.000000  0.983041  0.755529  0.000000  0.000000
4  0.421687  0.735280  0.000000  0.000000  0.000000  0.000000
5  0.035230  0.287157  0.000000  0.710662  0.000000  0.000000
6  1.370792  0.366914  0.000000  0.968567  0.000000  0.000000
7  0.000000  1.114756  0.000000  1.481492  1.994043  0.000000

map函数专门针对单个序列内的所有元素进行操作,有点儿类似于apply指定序列后的操作。

Init signature: map(self, /, *args, **kwargs)
Docstring:     
map(func, *iterables) --> map object

Make an iterator that computes the function using arguments from
each of the iterables.  Stops when the shortest iterable is exhausted.
Type:           type
df['A'].map(lambda x: round(x))
Out[34]: 
0    01    12   -13    04    05    06    17    0

Name: A, dtype: int64
df['B'].map(lambda x: np.abs(x))
Out[36]: 
0    1.7297831    0.2567862    0.3177753    0.7317884    0.7352805    0.2871576    0.3669147    1.114756

Name: B, dtype: float64
df['C'].map(lambda x: '%.2f' % x)

Out[37]: 
0    -0.251    -1.172     0.163     0.984    -0.625    -1.016    -0.707    -0.06

Name: C, dtype: object
df['D'].map(lambda x: x if x > 0 else 0)

Out[38]: 
0    0.0000001    0.0000002    0.0000003    0.7555294    0.0000005    0.7106626    0.9685677    1.481492

Name: D, dtype: float64

本文小结:

R

apply

Python


apply
applymap
map

往期案例数据请移步本人GitHub: https://github.com/ljtyduyu/DataWarehouse/tree/master/File