【Pandas学习笔记Task01】:python基础

这篇博客介绍了Python的基础知识,包括列表推导式、隐匿函数、map()、zip()和enumerate()的用法。接着讲解了Numpy库,涉及np数组的创建、转置、合并、切片、索引和常用函数。通过实例展示了矩阵乘法、更新矩阵、卡方统计量的计算以及求最大连续整数子数组长度的方法,强调了Numpy的高效性和简便性。
摘要由CSDN通过智能技术生成

前言

第一次参与组对学习,第一次写博客,不足之处也希望各位大佬批评指正。

python基础

列表推导式与条件赋值

列表推导式是用来简化代码书写的,需要有表达式,用于列表赋值;需要for循环,用于提供值。他还可以结合相关条件赋值语句(如if)进行滤值。
通常列表推导式有三种形式:

# 1、将for循环遍历的值i传给前面的表达式从而给列表赋值。
num = [i * 2 for i in range(5)]	# [0, 2, 4, 6, 8]

# 在1基础上通过if过滤奇数。
num = [i * 2 for i in range(5) if i % 2 == 0]	# [0, 4, 8]

# 在1基础上通过if-else将奇数置-1而不是扩大二倍。
num = [i*2 if i % 2 == 0 else -1 for i in range(5)]	# [0, -1, 4, -1, 8]

这里需要注意:if-else只能出现在for前面;而for后面只能有if。这就决定了不同位置的条件赋值存在不同的作用。

隐匿函数和map()

隐匿函数lambda与定义def相比是简洁的,而且不需要在意函数名,只需要关注映射关系。它的格式可以用下面简单的x+y理解:

lambda x , y : x+y	# lambda 参数: 对参数执行的操作
(lambda x, y :x + y)(3, 4)	# 7

而map()会根据提供的函数对指定序列做映射,返回的是map对象,可以用list()转化为列表。所以可以结合map与隐匿函数使用。

# 将list的元素分别代入func中,得到一个map对象,可以用list转为列表
map(func,list)

list(map(lambda x: x *2, range(5)))	# [0, 2, 4, 6, 8]

zip()与enumerate()

zip()用于将多个迭代对象相同位置(此位置的标准是按照原迭代对象低一维来算的)的元素连接成一个新的元组,返回zip对象。所以同样地,可以用list实现转换为列表的格式。

a = [[1,2],[3,4]]
b = [[2,2],[3,3]]
c = [[2,2],[3,3]]
list(zip(a, b, c))	# [([1, 2], [2, 2], [2, 2]), ([3, 4], [3, 3], [3, 3])]

zip()可以实现一个压缩结合的操作,而zip(*)可以实现解压操作,所以可用来求矩阵的转置,不过里面一维是按元组来存的。

a = [[1,2,3],[4,5,6]]
list(zip(*a))	# [(1, 4), (2, 5), (3, 6)]

enumerate()是可以实现在遍历过程中,不仅遍历值,还可以得到值所在的索引。

L = ['a', 'b', 'c', 'd']
for index, value in enumerate(L):
    print(index, value)	
    # 0 a
	# 1 b
	# 2 c
	# 3 d

numpy基础

numpy是一个支持大量的维度数组与矩阵运算的库,用它来算数美滋滋。

构造np数组

np.array([1,2,3])
np.linspace(1,5,11,endpoint = True)	#起点,终点,样本个数。(包括endpoint,但可以设置endpoint=False去掉endpoint.)
np.arange(1,5,2)	# (起点=0),终点,(步长=1)。(不包括endpoint.)

arrange 适用于知道序列中相邻两数之间的间隔的情况下,比如生成一定范围内奇数或者偶数的序列。linspace
适合序列长度和序列取值范围已知的情况。比如采样频率为1200 Hz, 也就是说 0~1s 之间有1200 个点。

还有一些特殊的矩阵。如全零,全1,单位矩阵等。

np.zeros((3, 3))	# 3行3列全0矩阵
np.ones((3, 3))	# 3行3列全1矩阵
np.eye(3)	# 3行3列的单位矩阵

还有一些随机矩阵。

np.random.rand()	# (0-1均匀分布)这里不要传元组,直接指定不同维度的个数就行
np.random.randn()	# 0~1标准正态分布
np.random.randint(low,high,size)	# 指定生成随机整数的最小值最大值和维度大小
np.random.choice()	# 可以从给定的列表中,以一定概率和方式抽取结果,当不指定概率时为均匀采样,默认抽取方式为有放回抽样
np.random.seed(0) 	# 设置种子,就相当是设定了随机值,之后每次随机都一样

np数组的转置与合并

(1) 转置为T

num = np.zeros((2, 3)).T 	# 这是转置矩阵

(2) 行合并与列合并

'''
np.r_[a,b] 连接行,就是将a与b对应的行连接;np.c_[a,b]连接列,就是将a与b对应的列连接。
如果是二维的a与b,就将一维元素作为单位进行连接;如果是一维的a与b,就将每个元素作为单位进行连接。
这里和之前的zip()合并时选取元素的规则有点相似。
我的理解是:
保持其合并前后的维度相同。就比如两个一维的合并,还是一维;两个二维的合并还是二维。不同维度合并会报错。
'''
# 以1维和2维举例如下:
# np.c_ 按列合并
a = np.array([[1, 2, 3],[7,8,9]])
b=np.array([[4,5,6],[1,2,3]])
c=np.c_[a,b]	# array([[1, 2, 3, 4, 5, 6],[7, 8, 9, 1, 2, 3]])
d= np.array([7,8,9])
e=np.array([1, 2, 3])
f=np.c_[d,e]	# array([[7, 1],[8, 2],[9, 3]])
# np.r_ 按行合并
a = np.array([[1, 2, 3],[7,8,9]])
b=np.array([[4,5,6],[1,2,3]])
c=np.r_[a,b]	# [[1, 2, 3],[7,8,9],[4,5,6],[1,2,3]]
d= np.array([7,8,9])
e=np.array([1, 2, 3])
f=np.r_[d,e]	# [7,8,9,1,2,3]

(3)维度变换 reshape

num = np.ones((3,1))	
# 这里因为reshape后元素个数要相匹配,所以最后一维是定的,所以可以在最后一维指定-1.
num = num.reshape(-1) 	# 这是将n行1列列表转换为1维的方法

np数组的切片与索引

就是利用一种简便的方法来选择数组中的元素值或者得到它的索引。这在预处理数据集时需要用到。

target = np.arange(9).reshape(3, 3)
target1 = target[:-1, [0, 2]] # 不包括最后一行,选择每一行的第0号和第2号元素。
target2 = target[np.ix_([0, 1], [True, False, True])]	# 以布尔索引和值索引形式给出,选择第0行和第1行,每一列的第0号元素和第2号元素。

np数组的常用函数

(1)where

np.where(condition, yes, no)	# 根据condition,true执行yes,否则执行no
np.where(condition) 	# 就一个条件,返回的是符合条件的索引
# 返回索引的特点(下同):原数组几维索引tuple就几维。组合每一维的对应位置就是符合条件的元素坐标

where的例子:

a = np.arange(7)
print(np.where(a <= 5, a, 5)) 	# 前提是a要为np生成的,否则会报错,把列表当做整体而无法执行列表内元素的比较。
# [0 1 2 3 4 5 5]
print(np.where(a > 5)) # 返回的是a列表的索引
# (array([6], dtype=int64),)
print(a[np.where(a <= 5)]) # 等价于 a[a<=5]
# [0 1 2 3 4 5]
b = np.arange(27).reshape(3, 3, 3)
print(np.where(b > 5)) # np.where(condition)返回的是索引。原数组几维索引tuple就几维。组合每一维的对应位置就是符合条件的元素坐标
# (array([0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2],dtype=int64),array([2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2],dtype=int64), array([0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2],dtype=int64))

(2)argmax,argmin,nonzero,cumsum, diff

a = np.array([[1, -1, 0], [-3, 3, 4]])
print(np.diff(a)) # 求与前一个元素差,len比原数组小1.全局方法,只能调用np来实现访问diff.
print(a.nonzero())  # (array([0, 0, 1, 1, 1], dtype=int64), array([0, 1, 0, 1, 2], dtype=int64))
# 这里最大最小的索引如果不指明,默认是按行来遍历索引,比如第二行第3列是4,最大,所以返回索引5.
#如果指明0或者1,就代表按列或者按行来找。
print(a.argmax())   # 5 返回最大数的索引
print(a.argmin())   # 3 返回最小数的索引
print(a.cumsum())   # 累加 [ 1  0  0 -3  0  4]
print(a.sum(0)) # [-2  2  4]
print(a.sum(1)) # [0 4]

练习

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

一般的矩阵乘法根据公式,可以由三重循环写出:

np.random.seed(0)
M1 = np.random.randint(1, 9, (2, 3))
M2 = np.random.randint(1, 9, (3, 4))
res = np.empty((M1.shape[0], M2.shape[1]))
for i in range(M1.shape[0]):
    for j in range(M2.shape[1]):
        item = 0
        for k in range(M1.shape[1]):
            item += M1[i][k] * M2[k][j]
        res[i][j] = item
print((np.abs((M1.dot(M2) - res) < 1e-15)).all())   # True 所有元素都符合
print(res)	# [[110.  70.  56. 114.][ 56.  24.  26.  56.]]

利用列表推导式实现:

res = [sum(M1[i] * M2.T[j]) for i in range(M1.shape[0]) for j in range(M2.shape[1])]
# [110, 70, 56, 114, 56, 24, 26, 56]

这里我将M1的行当做一个整体,M2的列当做整体来直接进行一维数组的相乘,得到的还是一维数组,然后求和得到值。因为numpy不利用点乘的话,相乘就是逐位相乘的。但实现过程中发现,计算的数值是正确的,但是无法分出维度来。因为惯性思维,先遍历i后遍历j我就先for i然后for j。看到参考答案后,才发现原来是这么玩的:

res = [[sum(M1[i] * M2.T[j]) for j in range(M2.shape[1])] for i in range(M1.shape[0])]
# [[110, 70, 56, 114], [56, 24, 26, 56]]

先遍历i不是嘛,那就让i在后面,让j在一个维度内遍历。这样当一个i对应的j遍历结束后,就代表当前第i行与所有的j列都计算完成了,也就是得到了(i,0),(i,1)…(i,n)的值,那正好这就是一个维度,用[]框住。太妙了。学习了~

EX2 更新矩阵

设矩阵 Am×n ,现在对 A 中的每一个元素进行更新生成矩阵 B ,更新方法是 Bij= B i j = A i j ∑ k = 1 n 1 A i k \displaystyle B_{ij}=A_{ij}\sum_{k=1}^n\frac{1}{A_{ik}} Bij=Aijk=1nAik1,例如下面的矩阵为 A ,则 B 2 , 2 = 5 × ( 1 4 + 1 5 + 1 6 ) = 37 12 B_{2,2}=5\times(\frac{1}{4}+\frac{1}{5}+\frac{1}{6})=\frac{37}{12} B2,2=5×(41+51+61)=1237,请利用 Numpy 高效实现。

A = np.arange(1, 10).reshape(3, -1)
B = np.empty((A.shape[0], A.shape[1]))
# # 一般方法
for i in range(A.shape[0]):
    for j in range(A.shape[1]):
        B[i][j] = A[i][j] * sum(1.0/A[i])
print(B)
# 利用numpy,太妙了
B = A * (1/A).sum(1).reshape(3, -1) # 这里reshape的是(1/A).sum(1),然后利用广播机制,实现A *() 的逐位相乘,这是numpy的特性。
print(B.shape)  # (3, 3)
print(B)

利用numpy的更新方法很妙。一开始没明白reshape的作用。因为本来维度是(3, ),这里要转换成3行1列,代表二维数组。否则(1/A).sum(1)其实是一个长度为3的一维数组。在广播的时候会出问题(正确的应该是向右,而一维会向下广播)。

EX3 卡方统计量

设矩阵 A m × n A_{m\times n} Am×n,记 B i j = ( ∑ i = 1 m A i j ) × ( ∑ j = 1 n A i j ) ∑ i = 1 m ∑ j = 1 n A i j B_{ij} = \frac{(\sum_{i=1}^mA_{ij})\times (\sum_{j=1}^nA_{ij})}{\sum_{i=1}^m\sum_{j=1}^nA_{ij}} Bij=i=1mj=1nAij(i=1mAij)×(j=1nAij),定义卡方值如下:
χ 2 = ∑ i = 1 m ∑ j = 1 n ( A i j − B i j ) 2 B i j \chi^2=\sum_{i=1}^m\sum_{j=1}^n\frac{(A_{ij}-B_{ij})^2}{B_{ij}} χ2=i=1mj=1nBij(AijBij)2
请利用 Numpy 对给定的矩阵 A 计算 χ 2 \chi^2 χ2

np.random.seed(0)
A = np.random.randint(10, 20, (8, 5))
B = np.empty((A.shape[0], A.shape[1]), dtype=list)
# B = A.sum(0)*A.sum(1).reshape(-1, 1)/A.sum()    # 答案方法
# print(B)
B = np.array([[np.sum(A[i])*np.sum(A.T[j])/np.sum(A) for j in range(A.shape[1])] for i in range(A.shape[0])]) # 我的代码略微复杂了
x2 = np.sum(np.square(A - B) / B)
print(x2)

我的想法就是两步,先求B再计算卡方。因为从EX1中得到学习,知道了怎么计算某元素行与列相乘,就用上了(列表推导式,这回记得将i的迭代放后面了)。做完还得意了一会儿,但看了答案,emm,不得不说给的太妙了,一句搞定,但对初学者简直很painful。

EX5 求最大连续整数的长度

输入一个整数的 Numpy 数组,返回其中递增连续整数子数组的最大长度。例如,输入 [1,2,5,6,7],[5,6,7]为具有最大长度的递增连续整数子数组,因此输出3;输入[3,2,1,2,3,4,6],[1,2,3,4]为具有最大长度的递增连续整数子数组,因此输出4。请充分利用 Numpy 的内置函数完成。(提示:考虑使用 nonzero, diff 函数)

h = input('please input a series of numbers:\n',) # 可以用split实带现空格或者逗号的输入,从而分隔
h_list = np.array(list(map(int, h)))
f = lambda x : np.diff(np.nonzero(np.r_[1, np.diff(x) != 1, 1])).max()
print(f(h_list))

这算是一种简单方法吧,我能想到的是枚举然后找连续(我能想到的大家都能想到,大家想到的我想不到,一个字:菜),O(n^2)复杂度,太难了。完全没想到,利用numpy可以如此简单的来做。主要是觉得这种思维很妙。
首先将差为1(代表连续)的元素在diff()后置为0,然后得到不为0的元素的索引(这里就是前后多加个1的妙处,可以保持元素的绝对位置),也就可以代表不连续的数字的位置,再利用diff求差(这里相当于求连续的长度),得到最大值那就是最大连续整数的长度了。太妙了~差距体现的淋漓尽致。
总结:第一次参与组对学习的第一篇博客,也是第一次写博客,很多不足的地方,如果有大佬看到还请多多批评指正。
通过练习感受到,numpy的强大和简便之处。不过距离那种程度还有很长的路要走,单是理解起来都需要一段时间,更别提自己去按照那种方法实现了。只能说日后努力学习吧。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值