Python程序设计 实验8:Numpy 和标准库

Posted 上山打老虎D

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python程序设计 实验8:Numpy 和标准库相关的知识,希望对你有一定的参考价值。

实验过程及内容(实验思路,代码实现过程以及运行并测试结果):

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这些都很大程度上方便了编程。
       此外,在本次实验过程中,我也遇到了一些问题:

  1. 在完成对图片的处理时调用了image.shape结果抛出异常。在查阅资料后发现这是读取的图片格式和PIL中Image 读取的图片格式差异问题导致的,解决方法就是在save操作之前,将图片的格式转化一下即可解决问题。

  2. 在完成第6题读入Python代码文件时,抛出如下异常:
    在这里插入图片描述
           通过查阅资料发现,发生该异常的原因是编码格式不是Python可以读取的格式,只需在读入的地方加上“encoding=utf-8”即可。

       此外,在本次实验中我发现,代码的容错性也是编程过程中需要注意的点。在进行实验六的过程中,多次由于路径不对造成异常的发生,因此需要在程序执行前加入判断语句,对路径有效性进行检验。

以上是关于Python程序设计 实验8:Numpy 和标准库的主要内容,如果未能解决你的问题,请参考以下文章

numpy basic sheatsheet

python可以做数据分析吗?

python常用到哪些库?

『Python』Numpy学习指南第十章_高端科学计算库scipy入门(系列完结)

python常用到哪些库

python学习之numpy