datawhale的pandas学习第一章

1.Python基础语法

1.1 列表推导式与条件赋值

列表推导式: [* for i in *] 。其中,第一个 * 为映射函数,其输入为后面 i 指代的内容,第二个 * 表示迭代的对象。

带有 if 选择的条件赋值,其形式为 value = a if condition else b

value = 'cat' if 2>1 else 'dog' #'cat'

结合两者

L = [1, 2, 3, 4, 5, 6, 7]
[i if i <= 5 else 5 for i in L] #[1, 2, 3, 4, 5, 5, 5]

1.2 匿名函数

在Python中,lambda的语法是唯一的。其形式如下:

lambda argument_list: expression

其中,lambda是Python预留的关键字,argument_list和expression由用户自定义:

(lambda x: x**2 + 3*x + 1) (5)  #5是传入的参数

由于lambda语法是固定的,其本质上只有一种用法,那就是定义一个lambda函数。在实际中,根据这个lambda函数应用场景的不同,可以将lambda函数的用法扩展为以下几种:

  1. 将lambda函数赋值给一个变量,通过这个变量间接调用该lambda函数

    例如,执行语句add=lambda x, y: x+y,定义了加法函数lambda x, y: x+y,并将其赋值给变量add,这样变量add便成为具有加法功能的函数。例如,执行add(1,2),输出为3。

  2. 将lambda函数赋值给其他函数,从而将其他函数用该lambda函数替换

例如,为了把标准库time中的函数sleep的功能屏蔽(Mock),我们可以在程序初始化时调用:time.sleep=lambda x:None。这样,在后续代码中调用time库的sleep函数将不会执行原有的功能。例如,执行time.sleep(3)时,程序不会休眠3秒钟,而是什么都不做。

  1. 将lambda函数作为其他函数的返回值,返回给调用者。

函数的返回值也可以是函数。例如return lambda x, y: x+y返回一个加法函数。这时,lambda函数实际上是定义在某个函数内部的函数,称之为嵌套函数,或者内部函数。对应的,将包含嵌套函数的函数称之为外部函数。内部函数能够访问外部函数的局部变量,这个特性是闭包(Closure)编程的基础,在这里我们不展开。

  1. 将lambda函数作为参数传递给其他函数

部分Python内置函数接收函数作为参数。典型的此类内置函数有这些。

  • filter函数。此时lambda函数用于指定过滤列表元素的条件。filter函数的主要作用是通过function对iterable中的元素进行过滤,并返回一个迭代器(iterator),其中是function返回True的元素。如果function传入None,则返回所有本身可以判断为True的元素。

    list(filter(None, [1, 0, 3])) #[1,3]
    list(filter(lambda x: x % 3 == 0, [1, 2, 3])) #[3]
    
  • sorted函数。此时lambda函数用于指定对列表中所有元素进行排序的准则。

    sorted([1, 2, 3, 4, 5, 6, 7, 8, 9], key=lambda x: abs(5-x))
    
  • map函数。此时lambda函数用于指定对列表中每一个元素的共同操作。

    list(map(lambda x: x+1, [ 1 , 2 , 3 ])) #[2,3,4]
    #对于多个输入值的函数映射,可以通过追加迭代对象实现:
    list(map(lambda x, y: str(x)+'_'+y, range(5), list('abcde')))
    
  • reduce函数。此时lambda函数用于指定列表中两两相邻元素的结合条件。

    reduce函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。

    语法:reduce(function, iterable[, initializer])

    将列表中的元素用逗号连起来

    from functools import reduce
    reduce(lambda a, b: '{}, {}'.format(a, b), [1, 2, 3, 4, 5, 6, 7, 8, 9])
    

另外,部分Python库函数也接收函数作为参数,例如gevent的spawn函数。此时,lambda函数也能够作为参数传入。

1.3 zip对象与enumerate方法

zip函数能够把多个可迭代对象打包成一个元组构成的可迭代对象,它返回了一个 zip 对象,通过 tuple, list 可以得到相应的打包结果:

In [18]: L1, L2, L3 = list('abc'), list('def'), list('hij')

In [19]: list(zip(L1, L2, L3))
Out[19]: [('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]

In [20]: tuple(zip(L1, L2, L3))
Out[20]: (('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j'))

enumerate 是一种特殊的打包,它可以在迭代时绑定迭代元素的遍历序号:

In [22]: L = list('abcd')

In [23]: for index, value in enumerate(L):
   ....:     print(index, value)
   ....: 
0 a
1 b
2 c
3 d

zip 对象也能够简单地实现这个功能:

In [24]: for index, value in zip(range(len(L)), L):
   ....:     print(index, value)
   ....: 
0 a
1 b
2 c
3 d

Python 也提供了 * 操作符和 zip 联合使用来进行解压操作(拆包):

In [26]: zipped = list(zip(L1, L2, L3))

In [27]: zipped
Out[27]: [('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]

In [28]: list(zip(*zipped)) # 三个元组分别对应原来的列表
Out[28]: [('a', 'b', 'c'), ('d', 'e', 'f'), ('h', 'i', 'j')]

zip函数可以用于矩阵行列互换:

x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
list(map(list,zip(*x))) #[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

2Numpy基础

2.1.np数组的构造

2.1.1np.array()详解

参考:https://blog.csdn.net/sinat_28576553/article/details/89047893

numpy的数据类型(可以定义结构化数据类型)

参考:https://www.runoob.com/numpy/numpy-dtype.html

定义一个结构化数据类型 student,包含字符串字段 name,整数字段 age,及浮点字段 marks,并将这个 dtype 应用到 ndarray 对象

import numpy as np
student = np.dtype([('name','S20'), ('age', 'i1'), ('marks', 'f4')]) 
print(student) #[('name', 'S20'), ('age', 'i1'), ('marks', 'f4')]
a = np.array([('abc', 21, 50),('xyz', 18, 75)], dtype = student) 
print(a) #[('abc', 21, 50.0), ('xyz', 18, 75.0)]

np.asarray与np.array辨析

**相同点:**array和asarray都可以将数组转化为ndarray对象。**区别:**当参数为一般数组时,两个函数结果相同;当参数本身就是ndarray类型时,array会新建一个ndarray对象,作为参数的副本,但是asarray不会新建,而是与参数共享同一个内存。重点就是这个共享内存。

np.array与np.ndarray辨析

numpy.array只是一个创建ndarray的便利函数;它本身不是一个类。他讲到也可以使用numpy.ndarray创建一个数组,但这不是推荐的方法。 numpy.ndarray() 是一个类,而numpy.array() 是一个创建ndarray的方法/函数。

下面讨论一些特殊数组的生成方式:

【a】等差序列: np.linspace, np.arange

【b】特殊矩阵: zeros, eye(单位矩阵), full, ones

【c】随机矩阵: np.random

最常用的随机生成函数为 rand, randn, randint, choice ,它们分别表示0-1均匀分布的随机数组、标准正态的随机数组、随机整数组和随机列表抽样。

np.random.choice

  • numpy.random.choice(a, size=None, replace=True, p=None)
  • 从a(只要是ndarray都可以,但必须是一维的)中随机抽取数字,并组成指定大小(size)的数组
  • replace:True表示可以取相同数字,False表示不可以取相同数字
  • 数组p:与数组a相对应,表示取数组a中每个元素的概率,当不指定概率时为均匀采样,默认抽取方式为有放回抽样。

当返回的元素个数与原列表相同时,等价于使用 permutation 函数,即打散原列表。

np.random.seed() 使后面的随机数按照一定的顺序生成

1.随机数种子对后面的结果一直有影响。同时,加了随机数种子以后,后面的随机数组都是按一定的顺序生成的

2.随机数种子里的参数只是确定一下随机数的起始位置

2.2 np数组的变形与合并

【a】转置: T、transpose、swapaxes

1.数组转置(T)

import numpy as np
np.zeros((2,3)).T

2.轴对换之transpose
对于高维数组,可以使用轴对换来对多个维度进行变换。

transpose进行的操作其实是将各个维度重置,原来(2,3,4)对应的是(0,1,2)。使用transpose(1,0,2)后,各个维度大小变为(3,2,4),其实就是将第一维和第二维互换。

对于这个三维数组,转置T其实就等价于transpose(2,1,0)

a = np.arange(24).reshape((2,3,4))
b=a.T
print(b.shape) #(4,3,2)
c=a.transpose(1,0,2)
print(c.shape) #(3,2,4)

3.两轴对换swapaxes:swapaxes方法接受的参数是一对轴编号,使用transpose方法是对整个轴进行对换,而swapaxes是将参数的两个轴进行对换。刚刚上面的transpose(1,0,2),实际上就是将0和1轴进行对换,因此使用swapaxes也可以实现,如下:

d=a.swapaxes(0,1)
print(d.shape) #(3,2,4)

【b】合并操作

concatenate提供了axis参数,用于指定拼接方向
append默认先ravel再拼接成一维数组,也可指定axis
stack提供了axis参数,用于生成新的维度
hstack水平拼接,沿着行的方向,对列进行拼接
vstack垂直拼接,沿着列的方向,对行进行拼接
dstack沿着第三个轴(深度方向)进行拼接
column_stack水平拼接,沿着行的方向,对列进行拼接
row_stack垂直拼接,沿着列的方向,对行进行拼接
r_垂直拼接,沿着列的方向,对行进行拼接
c_水平拼接,沿着行的方向,对列进行拼接

【注意】

1.stack,hstack,vstack,column_stack,row_stack只能接受一个参数也就是一个tuple**,所以使用这两个函数必须得有双重括号.

其中:stack(arrays, axis=0, out=None) ,对制定axis增加维度

import numpy as np
ar1 = np.array([[1,2,3], [4,5,6]])
ar2 = np.array([[7,8,9], [11,12,13]])
ar1
np.stack((ar1, ar2))   # 增加第一个维度(axis0,之后的axis向后顺延:0—>1, 1—>2)
'''array([[[ 1, 2, 3],
    [ 4, 5, 6]],
    [[ 7, 8, 9],
    [11, 12, 13]]])
 '''
np.stack((ar1, ar2), axis=1)   # 增加第二个维度(axis1,之后的axis向后顺延, 1—>2)
'''array([[[ 1, 2, 3],
    [ 7, 8, 9]],
    [[ 4, 5, 6],
    [11, 12, 13]]])
    '''
np.stack((ar1, ar2), axis=2)   # 增加第三个维度(axis2,和axis=-1的效果一样,原来的axis0和axis1保持不变)
'''array([[[ 1, 7],
    [ 2, 8],
    [ 3, 9]],
    [[ 4, 11],
    [ 5, 12],
    [ 6, 13]]])'''

2.hstack(水平拼接),vstack(垂直拼接),dstack(沿着第三轴(深度方向)进行拼接, 效果相当于stack(axis=-1)

3.np.r_np.c_后面是用[] ,且分别与row_stack和column_stack效果一致。

注意:一维数组和二维数组进行合并时,应当把其视作列向量,在长度匹配的情况下只能够使用左右合并的 c_ 操作

【c】维度变换: reshape

reshape 能够帮助用户把原数组按照新的维度重新排列。在使用时有两种模式,分别为 C 模式和 F 模式,分别以逐行和逐列的顺序进行填充读取。

2.3np数组的切片与索引

数组的切片模式支持使用 slice 类型的 start:end:step 切片,还可以直接传入列表指定某个维度的索引进行切片。

此外,还可以利用 np.ix_ 在对应的维度上使用布尔索引(传入列表),但此时不能使用 slice 切片。

当数组维度为1维时,可以直接进行布尔索引,而无需 np.ix_

2.4 常用函数

为了简单起见,这里假设下述函数输入的数组都是一维的。

【a】 where

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

【b】 nonzero, argmax, argmin

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

【c】 any, all

any指当序列至少存在一个True或非零元素时返回True,否则返回False

all指当序列全为True或非零元素时返回True,否则返回False

【d】 cumprod, cumsum, diff

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

In [87]: a = np.array([1,2,3])

In [88]: a.cumprod()
Out[88]: array([1, 2, 6], dtype=int32)

In [89]: a.cumsum()
Out[89]: array([1, 3, 6], dtype=int32)

In [90]: np.diff(a)
Out[90]: array([1, 1])

【e】 统计函数

常用的统计函数包括 max, min, mean, median, std, var, sum, quantile ,其中分位数计算是全局方法,因此不能通过 array.quantile 的方法调用(np.quantile(array,分位数)):

In [91]: target = np.arange(5)

In [92]: target
Out[92]: array([0, 1, 2, 3, 4])

In [93]: target.max()
Out[93]: 4

In [94]: np.quantile(target, 0.5) # 0.5分位数
Out[94]: 2.0

但是对于含有缺失值的数组,它们返回的结果也是缺失值,如果需要略过缺失值,必须使用 nan* 类型的函数,上述的几个统计函数都有对应的 nan* 函数。

In [95]: target = np.array([1, 2, np.nan])

In [96]: target
Out[96]: array([ 1.,  2., nan])

In [97]: target.max()
Out[97]: nan

In [98]: np.nanmax(target)
Out[98]: 2.0

In [99]: np.nanquantile(target, 0.5)
Out[99]: 1.5

对于协方差和相关系数分别可以利用 cov, corrcoef 如下计算:

In [100]: target1 = np.array([1,3,5,9])

In [101]: target2 = np.array([1,5,3,-9])

In [102]: np.cov(target1, target2) #协方差矩阵
Out[102]: 
array([[ 11.66666667, -16.66666667],
       [-16.66666667,  38.66666667]])

In [103]: np.corrcoef(target1, target2)#相关系数矩阵
Out[103]: 
array([[ 1.        , -0.78470603],
       [-0.78470603,  1.        ]])

最后,需要说明二维 Numpy 数组中统计函数的 axis 参数,它能够进行某一个维度下的统计特征计算,当 axis=0 时结果为列的统计指标,当 axis=1 时结果为行的统计指标:

In [104]: target = np.arange(1,10).reshape(3,-1)

In [105]: target
Out[105]: 
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

In [106]: target.sum(0)
Out[106]: array([12, 15, 18])

In [107]: target.sum(1)
Out[107]: array([ 6, 15, 24])

2.5 广播机制

广播机制用于处理两个不同维度数组之间的操作,这里只讨论不超过两维的数组广播机制。

【a】标量和数组的操作

当一个标量和数组进行运算时,标量会自动把大小扩充为数组大小,之后进行逐元素操作:

In [108]: res = 3 * np.ones((2,2)) + 1

In [109]: res
Out[109]: 
array([[4., 4.],
       [4., 4.]])

In [110]: res = 1 / res

In [111]: res
Out[111]: 
array([[0.25, 0.25],
       [0.25, 0.25]])

【b】二维数组之间的操作

当两个数组维度完全一致时,使用对应元素的操作,否则会报错,除非其中的某个数组的维度是 m×1 或者 1×n ,那么会扩充其具有 1的维度为另一个数组对应维度的大小。例如, 1×2 数组和 3×2 数组做逐元素运算时会把第一个数组扩充为 3×2 ,扩充时的对应数值进行赋值。但是,需要注意的是,如果第一个数组的维度是 1×3 ,那么由于在第二维上的大小不匹配且不为 1 ,此时报错。

In [112]: res = np.ones((3,2))

In [113]: res
Out[113]: 
array([[1., 1.],
       [1., 1.],
       [1., 1.]])

In [114]: res * np.array([[2,3]]) # 扩充第一维度为3
Out[114]: 
array([[2., 3.],
       [2., 3.],
       [2., 3.]])

In [115]: res * np.array([[2],[3],[4]]) # 扩充第二维度为2
Out[115]: 
array([[2., 2.],
       [3., 3.],
       [4., 4.]])

In [116]: res * np.array([[2]]) # 等价于两次扩充
Out[116]: 
array([[2., 2.],
       [2., 2.],
       [2., 2.]])

【c】一维数组与二维数组的操作

当一维数组 Ak 与二维数组 𝐵𝑚,𝑛 操作时,等价于把一维数组视作 A1,k 的二维数组,使用的广播法则与【b】中一致,当 k!=n 且 k,n 都不是 1时报错。

In [117]: np.ones(3) + np.ones((2,3))
Out[117]: 
array([[2., 2., 2.],
       [2., 2., 2.]])

In [118]: np.ones(3) + np.ones((2,1))
Out[118]: 
array([[2., 2., 2.],
       [2., 2., 2.]])

In [119]: np.ones(1) + np.ones((2,3))
Out[119]: 
array([[2., 2., 2.],
       [2., 2., 2.]])

2.6向量与矩阵运算

【a】向量内积:

1.两个一维数组的np.dot

【b】向量范数和矩阵范数: np.linalg.norm

参考:

https://blog.csdn.net/weixin_43977640/article/details/109909488

https://blog.csdn.net/hqh131360239/article/details/79061535

函数参数:

x_norm=np.linalg.norm(x, ord=None, axis=None, keepdims=False)

注意:向量的范数计算与 矩阵的范数计算不同

在矩阵范数的计算中,最重要的是 ord 参数,可选值如下:

ordnorm for matricesnorm for vectors
NoneFrobenius norm2-norm
‘fro’Frobenius norm
‘nuc’nuclear norm
infmax(sum(abs(x), axis=1))max(abs(x))
-infmin(sum(abs(x), axis=1))min(abs(x))
0sum(x != 0)
1max(sum(abs(x), axis=0))as below
-1min(sum(abs(x), axis=0))as below
22-norm (largest sing. value)as below
-2smallest singular valueas below
other s u m ( a b s ( x ) o r d ) 1 o r d sum(abs(x)^{ord})^{\frac{1}{ord}} sum(abs(x)ord)ord1

Frobenius norm : ∥ A ∥ F = [ ∑ i j a b s ( a i , j ) 2 ] 1 2 \left \| A \right \|_{F}=[\sum_{ij}abs(a_{i,j})^{2}]^{\frac{1}{2}} AF=[ijabs(ai,j)2]21,即对所有元素的绝对值的平方求和后开平方。

nuclear norm:所有奇异值的和

【c】矩阵乘法:

  1. 两个二维数组的np.dot,得到的结果是矩阵乘积。

  2. np.matmul

  3. @

    import numpy as np
    a=np.array([[1,2,3],
               [4,5,6]])
    b=np.array([[1,4],
                [2,5],
                [3,6]])
    print(np.dot(a,b))
    print(np.matmul(a,b))
    print(a@b)
    

【d】逐元素相乘

  1. np.multiply

  2. *运算符

例子:

import numpy as np
a=np.array([[1,2,3],
           [4,5,6]])
b=np.array([[1,2,3],
           [4,5,6]])
c=np.array([1,2,3])
d=np.array([1,2,3])
print(np.dot(c,d))
print(a*b)
print(np.multiply(a,b))

注意:c,d中结果不一致

3.练习

EX1:利用列表推导式写矩阵乘法

res=[]
M1 = np.random.rand(2,3)

M2 = np.random.rand(3,4)

res = np.array([sum([M1[i][k]*M2[k][j]for k in range(M1.shape[1])])for i in range(M1.shape[0])for j in range(M2.shape[1])]).reshape(M1.shape[0],M2.shape[1])

((M1@M2 - res) < 1e-15).all()
#先计算出来,在变化形状

答案:外面先构造出结果矩阵的行数列数,sum里面对列表推导式求和,这里可以分成两步走

res1 = [[sum([M1[i][k] * M2[k][j] for k in range(M1.shape[1])]) for j in range(M2.shape[1])] for i in range(M1.shape[0])]

EX2:更新矩阵

A=np.array([[1,2,3],
          [4,5,6],
          [7,8,9]])


(1/A).sum(axis=1).reshape(-1,1)

B=A*(1/A).sum(axis=1).reshape(-1,1)

B

注意:.点操作符的运算优先级高于乘法运算符

EX3:卡方统计量

np.random.seed(0)
A = np.random.randint(10, 20, (8, 5))
A
A.sum(axis=0)
A.sum(axis=1)
B=(A.sum(axis=1).reshape(-1,1)*A.sum(axis=0))/A.sum()
B.shape
C=(A-B)*(A-B)/B
C.sum()

还可以写成矩阵乘积形式

B1=A.sum(axis=1).reshape(-1,1)@A.sum(axis=0).reshape(1,-1)/A.sum()

EX4:改进矩阵计算的性能

答案很巧妙,将公式变形。

np.random.seed(0)

m, n, p = 100, 80, 50
B = np.random.randint(0, 2, (m, p))
U = np.random.randint(0, 2, (p, n))
Z = np.random.randint(0, 2, (m, n))

(((B**2).sum(1).reshape(-1,1) + (U**2).sum(0) - 2*B@U)*Z).sum()

自己:

np.random.seed(0)

m, n, p = 100, 80, 50
B = np.random.randint(0, 2, (m, p))
U = np.random.randint(0, 2, (p, n))
Z = np.random.randint(0, 2, (m, n))

K=((np.expand_dims(B, axis=1) - U.T)**2).sum(axis=2)

(K*Z).sum()

在这里插入图片描述

EX5:连续整数的最大长度

答案:

f = lambda x:np.diff(np.nonzero(np.r_[1,np.diff(x)!=1,1])).max()

自己做的只能处理最大长度数组在中间的,没想到可以在np.diff(x)前后都加上1。看了答案后启发了一下我,将输入的数组的第一位加上比原来第一位大的数,最后一位加上比原来最后一位小的数。

PS:也不知道对不对,试了几个特殊情况是对的·····

def max_num(arr):
    arr1=np.insert(np.append(arr,arr[-1]-1),0,arr[0]+1)
    b=np.nonzero(np.diff(arr1)-1)[0]
    return np.max(np.diff(b))

很多都不会,python和numpy要继续学了,很多东西都没查完。pandas第一次打卡很仓促····

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值