实验过程及内容(实验思路,代码实现过程以及运行并测试结果):
1. Numpy 基础:
运行以下代码,理解每句代码的意思或输出结果(print 语句):
(a) mysqrt = [math.sqrt(x) for x in range(0,5)] mycrt = [x**(1/3) for x in range(0,5)] npData = np.array(mysqrt) print(“The shape:”, npData.shape) print(“The dimensionality:”, npData.ndim) print(“The type:”, npData.dtype) twoDarray = np.array([mysqrt, mycrt]) print(“The shape:”, twoDarray.shape) print(“The dimensionality:”, twoDarray.ndim) print(“The type:”, twoDarray.dtype)
【代码解释】
npData.shape:shape函数的功能是查看矩阵或者数组的维数。
npData.ndim:ndim函数的功能是查看数组中每个元素的维数。
npData.dtype:dtype函数的功能是查看该数组中每个元素的数据类型。
因此,对于npData = np.array(mysqrt)数组的维数是5,每个元素是单独的浮点型数,因此每个元素的维数是1.又因为每个元素是浮点型小数,因此数据类型是float64。
同理,对于twoDarray = np.array([mysqrt,mycrt])数组的维数是2*5,每个元素是两个浮点型数组成的元组,因此每个元素的维数是2.又因为每个元素是浮点型小数,因此数据类型是float64。
【运行结果】
(b)
zeros = np.zeros(3) zMat = np.zeros((4,3)) ones = np.ones(3) oMat = np.ones((3,2)) diag = np.eye(4) rng = np.arange(5) dm = np.diag(rng) print(dm.shape) zMat_re = zMat.reshape(6,2)
【代码解释】
通过分析代码,可知最终起作用的代码只有如下三行的部分:
numpy.arange(start, stop, step, dtype = None)在给定间隔内返回均匀间隔的值。值在半开区间 [开始,停止]内生成(换句话说,包括开始但不包括停止的区间),返回的是 ndarray 。
np.diag()以一维数组的形式返回方阵的对角线(或非对角线)元素,或将一维数组转换成方阵(非对角线元素为0)
因此首先生成了一个数组[0,1,2,3,4],然后以这个数组为对角线生成了一个5*5的矩阵,再调用函数打印矩阵的维数,因此将输出(5,5)
【运行结果】
( c )
A = np.random.randint(0,10, size = (3,2)) B = np.random.randint(0,10, size = (3,3,3)) C = np.random.randint(0,10, size = (3,1)) print(A**2) print(np.sqrt(A)) print(A + C) print(B + C) B[:, 0:2 , 0:2 ] -= 20 print(B)
【代码解释】
np.random.randint(a,b, size)表示生成范围在a~b内的,维度为size的矩阵。
A**2表示将A中的每个元素平方。
np.sqrt(A)表示将A中的每个元素开平方。
A + C、B + C:表示将两个矩阵相加,将两个矩阵对应相加,当遇到两矩阵大小不一样的情况,两个矩阵的shape必须满足若干个小矩阵可以组成与大矩阵同大小的矩阵。此时,将小矩阵中的元素对应地加到大矩阵上。
B[:, 0:2 , 0:2 ] -= 20:通过切片选择第一维的全部,第二维的前两行,第三维的前两列中的每个元素各减20。
【运行结果】
由于本题中使用了随机数生成数据,因此本题的数据具有偶然性。本次运行结果只对如下生成的A、B、C三个数据:
A**2
numpy.sqrt(A)
A + C
B + C
B[:, 0:2 , 0:2 ] -= 20
(d)
from numpy import linalg A = np.array([[2, 1, -2],[1,-1,-1], [1, 1 ,3]]) b = np.array([3, 0, 12]) x = linalg.solve(A,b) print(x)
【代码解释】
首先创建了两个数组。
solve函数有两个参数a和b。a是一个N*N的二维数组,而b是一个长度为N的一维数组,solve函数找到一个长度为N的一维数组x,使得a和x的矩阵乘积正好等于b,数组x就是多元一次方程组的解。
【运行结果】
2. Numpy 应用 1:
给定一个矩阵 2n×2n,将该矩阵分成四个象限(参见示例),然后返回一个新的 2×2 矩阵,包含每个象限的平均值。
例子:
原矩阵:
新矩阵:
【解题思路】
对于本题,由于前面第一题已经介绍了切片,获取矩阵维度以及获取矩阵部分的累加和的方法,本题中可以直接使用。可以首先获取矩阵的维度,从而获取结果矩阵中的4个数分别与原矩阵的四个象限的对应关系。再通过切片选定区域后,使用sum进行求和,最后再除以维度即可。
【编程实现】
import numpy # 获取初始矩阵 matrix1 = numpy.array( [[1, 2, 5, 7], [4, 1, 8, 0], [2, 0, 5, 1], [0, 2, 1, 1]]) # 获得矩阵维度 n = (int)(matrix1.shape[0]/2) # 进行切片分割 res = numpy.array( [[matrix1[:n, :n].sum()/(n**2), matrix1[:n, n:].sum()/(n**2)], [matrix1[n:, :n].sum()/(n**2), matrix1[n:, n:].sum()/(n**2)]]) print(res)
首先利用shape函数获取矩阵的维度,并除以二获得四个象限的分界点。然后通过切片获取每个象限并求和。最后将每个象限的和除以象限中元素个数(n^2)即为平均值,然后再利用array函数将四个平均值转成数组的形式。
【运行测试】
3. Numpy 应用 2:
仿照课件中利用 numpy 处理图片的方法,选择一张自己喜欢的图片进行处理,方法不限(如调整亮度、彩色变黑白、模糊化等等;鼓励自学图片处理方法,提出自己的方法)。
【解题思路】
在本题中,我使用隔行取样对图片进行了压缩。当我们遇到图片需要压缩的情况,常常可以想到进行隔行取样的方法,完成对图片的压缩。
【编程实现】
# 引入所需的库 import numpy as np import matplotlib.pyplot as plt from PIL import Image # 打开图片文件 image = Image.open('test.jpg') # 将图片转成矩阵表示 image_array = np.array(image) # 进行图片压缩 image_array1 = image_array[::2, ::2, ] # 展示图片 plt.imshow(image_array1) plt.show()
首先打开图片文件,转成矩阵之后,采用切片进行隔行取样,此处需注意,隔行的值不能太大,否则图像会严重失真。取样后直接输出结果即可。
【运行测试】
原图:
压缩后的图:
可以看到,图片被压缩了。被压缩的图片比压缩前模糊了。
4. Python 标准库 itertools:
编写函数 sum0(lst),接受一个包含 8 个整数的列表lst。如果列表中的某些非空子集的总和返回 0,则返回 True。例如,lst=[-3, 11, 21, 5, 10, 11, 2, 1]返回 True,因为非空子集[-3, 2, 1]中数字加起来总和为 0;又如 lst=[2, 3, 4, 5, 6, 7, 8, 9]时,函数返回 False。
【解题思路】
首先,本题中需要完成的是对所有组合情况的累加和检查,因此需要获得所有组合情况。我们不妨利用itertools中的combinations获取确定长度下的组合的迭代器并对部分和进行判断,再利用一层循环对组合长度进行遍历。
【编程实现】
import itertools # 定义判定是否存在组合为0函数 def sum0(lst): # 获取不同长度的组合 for num in range(1, 9): # 获取相同长度下的不同组合迭代器 it = itertools.combinations(lst, num) # 遍历判断是否和为0 for i in it: if sum(i) == 0: return True return False lst = [1, 2, 3, 4, 5, 6, 7, -28] print(sum0(lst))
首先利用外层循环遍历每次获得的组合的长度,然后对于每个确定的长度通过itertools.combinations获取全部组合的迭代器,然后再利用一层循环遍历整个迭代器判断部分和是否为零,如果为零直接返回True。如果遍历完所有长度的所有组合后都没有部分和为零则返回False。
【运行测试】
当存在部分和为0时:
当不存在部分和为0时:
5. Python 标准库 datetime:
编写函数 calculate_age,计算自出生以来到目前为止,生活的总天数,总月数,总年数,返回元组(days,months,years);
【解题思路】
可以通过datetime获取当前时间并与自己的生日做差,将时间差转换为天数,然后再一次转换为对应的总年月日数。
【编程实现】
import datetime # 定义计算年龄函数 def calculate_age(): # 将生日转为datetime birthday = datetime.datetime(2000, 9, 28) # 获取datetime当前时间 cur = datetime.datetime.now() # 获得日期差 during_days = (cur-birthday).days # 获得对应年月日 return(during_days, (int)(during_days/30), (int)(during_days/365)) print(calculate_age())
首先获取当前时间并将自己的生日转为datetime类,并直接做差获得日期差,再利用日期差,获得对应的年月日的差并转为元组。
【运行测试】
6. Python 标准库 collections, sys, os: 统计目前写过的 python 代码。
(1) 把实验、作业、小测的代码文件整理好,分开放在“作业”文件夹、“实验”文件夹、“小测”文件夹,三个文件夹放到同一个文件夹“代码”中。
【解题思路】
按照要求整理对应文件即可:
【结果如下】
(2) 编写函数,统计“代码”文件夹中 python 文件的个数(file_num),写过的代码行数( code_line ),代码中空行的行数 ( space_lines ),注释的行数 (comments_lines,只统计以#开头的),返回元组记录上述信息。
【解题思路】
通过最外层循环遍历代码文件夹下的三个文件夹,对于每个文件夹,再使用一层循环遍历文件夹中的每个文件,对于每个文件,首先判断拓展名是不是‘.py’如果不是则继续下一个文件的判断。如果是,则按行读入文件,每读入一行,行计数器加一。对于每一行,分别进行空行与注释的判断,满足则对应计数器加一。当所有文件都判断完毕后,直接返回结果元组即可。
【编程实现】
import collections import sys import os def getInfo(path): # path = './code' # 定义各个计数器 file_num = 0 code_line = 0 space_lines = 0 comments_lines = 0 # 对文件路径非法情况进行特判 if not os.path.exists(path): print('Invalid Path!') return # 首先遍历“作业”文件夹、“实验”文件夹、“小测”文件夹 for i in os.listdir(path): # 遍历各个文件夹中的每个文件 for j in os.listdir(path+'/'+i): # 如果是Python文件,则对应计数器加一 if j.endswith('.py'): file_num += 1 # 按行读入Python文件 for line in open(path+'/'+i+'/'+j, encoding='utf-8'): # 对于每行 对应计数器加一 code_line += 1 # 如果为空行 则空行计数器加一 if line == "\n": space_lines += 1 # 如果为注释 则注释计数器加一 if line.startswith('#'): comments_lines += 1 # 返回结果元组 return(file_num, code_line, space_lines, comments_lines) print(getInfo('./code'))
首先判断路径的合法性,当出现非法路径,则直接给出错误信息并返回。对于合法的路径,通过外层循环遍历三个文件夹,再通过一层循环遍历各个文件。对于每个文件,首先判断文件的拓展名是否为‘.py’,如果不是则判断下一个,如果是,则按行将文件读入,再对每行进行空行和注释的判断。当处理完所有文件后,将结果以元组的形式返回。
【运行测试】
(3) 在(2)基础上,允许用户输入指定的文件或文件夹,统计输入文件或文件夹的信息。例如,假设 python 文件名为 code_stat.py,运行方法如下:
【解题思路】
与(2)思路大致相同,只需加入对路径或文件的判断即可,在此处,我使用os.path.isdir(path)和os.path.isfile(path)完成对路径或文件的判断。
【编程实现】
import collections import sys import os def getInfo(path): # path = './code' file_num = 0 code_line = 0 space_lines = 0 comments_lines = 0 if not os.path.exists(path): print('Invalid Path!') return # 如果是路径 if os.path.isdir(path): for j in os.listdir(path): if j.endswith('.py'): file_num += 1 for line in open(path+'/'+j, encoding='utf-8'): code_line += 1 if line == "\n": space_lines += 1 if line.startswith('#'): comments_lines += 1 # 如果是Python文件 elif os.path.isfile(path) and path.endswith('.py'): file_num += 1 for line in open(path, encoding='utf-8'): code_line += 1 if line == "\n": space_lines += 1 if line.startswith('#'): comments_lines += 1 return(file_num, code_line, space_lines, comments_lines) print(getInfo('./code/Homework'))
大致思路与(2)相同,区别只是对传入的路径进行了判断,如果是路径,则依次遍历路径中的文件进行统计;如果是文件,则直接读取文件进行统计。
【运行测试】
对路径的测试:
对文件的测试:
实验结论:
在本次实验中,我学习并学会了Numpy库的一些函数的使用方法,与之前学过的C++相比,Python多元化的库为Python处理更多数据提供了良好的技术支持。Numpy库中涵盖广泛,可以处理矩阵,线性代数等,也可以处理图片。这些都方便了我使用Python完成对数据的进一步处理。
我也学会了使用Python的标准库itertools,datetime,collections, sys,和os这些都很大程度上方便了编程。
此外,在本次实验过程中,我也遇到了一些问题:
在完成对图片的处理时调用了image.shape结果抛出异常。在查阅资料后发现这是读取的图片格式和PIL中Image 读取的图片格式差异问题导致的,解决方法就是在save操作之前,将图片的格式转化一下即可解决问题。
在完成第6题读入Python代码文件时,抛出如下异常:
通过查阅资料发现,发生该异常的原因是编码格式不是Python可以读取的格式,只需在读入的地方加上“encoding=utf-8”即可。
此外,在本次实验中我发现,代码的容错性也是编程过程中需要注意的点。在进行实验六的过程中,多次由于路径不对造成异常的发生,因此需要在程序执行前加入判断语句,对路径有效性进行检验。