最近在学python面向对象 正好也在学数据分析与可视化
正好就拿题目来练练手
如题
这是附件内的数据
题目要求是输出对应的几个值 出于挑战,这里想要写一个类来完成对这类成绩单的处理,让其对其他的类似成绩单也有作用
所以程序的可用性有一定要求
举例来说
比如说这个文件第二列是没有用的 需要跳过,但是别的文件可能不需要跳过 或者需要跳过更多列数,所以在处理这个问题的时候就需要考虑到到底需要跳过多少列
我在代码中要求输入了需要跳过的列数来达到这个目的
当然还有很多和上面一样需要考虑的地方
# 用于读取csv文件内的成绩单 运算结果后返回
# 支持多种运算
# 本来准备自己写运算的 但是毕竟别人都写好了 不用白不用
# author = WHUT_L1_Sta2
# 希望以后能够设计一个UI出来
# ver = 1.0
import numpy as np
class scores:
'''
类内部需要实现的方法
1 读取 多行多列的成绩表(csv)
2 检索 找到关键词对应的行或列(这点在np里面不好实现)
3 计算 计算多种值 为了方便直接调用np的库算了
4 输出 任意类中的数值
'''
首先是开头,定义一个类
出于个人习惯 我把要在类内部实现的方法写在了类下方的注释里
先来实现前几个功能
def __init__(self, lines: int, rows: int) -> None:
'''
简单获取三个值以初始化
行数 列数 路径
'''
self.name = input('请输入需要计算成绩的人名')
self.subject = input('请输入需要计算成绩数据的科目')
self.lines = lines
self.rows = rows
# 这俩也可以用input() 相应的 就不要初始化参数了
# 这里这样写是因为想要两种方法都试一下
def my_read(self, csv_name) -> None:
'''
传入文件名以开始
暂时不提供间隔选项,会在之后看需求更新
默认逗号和回车'''
with open(csv_name, encoding='utf-8') as f:
self.data = f.read().replace('\n', ',').split(',')
def my_find(self) -> None:
'''
查找人名和科目所对应的行列数
在转化为二维数组之后很难实现
我把它放到了自带的读取文件后
并且把它放在转换二维数组之前
'''
self.need_line = self.data.index(self.subject)
self.need_row = self.data.index(self.name) // (self.lines - 1)
# 这里计算出是第几行 因为上面得到的是一个一维的列表
def my_array(self) -> None:
'''转化为二维数组'''
self.array = np.array(self.data).reshape(self.rows, self.lines)
# 根据输入的行列数转换为二维数组
类里面的第一个方法是初始化,在这里我要求输入文件的行数,列数和文件路径并且对参数完成了赋值,用self可以完成参数在类内部不同方法里的传递。值得一提的是本来可以直接计算出文件的行列数,但是我没那样做,因为那样需要先转换为二维数组,但是那样就不太好实现对列表行列名的查找,所以这里要求直接输入
类里面的第二个方法是读文件 这里直接用系统的来读,将所有回车换为逗号后用逗号分隔
类里面的第三个方法是查找 用于检索列名或者行名所对应的列或者行的索引数,这里如果先转为二维数组就难以进行索引了
类里面的第四个方法是转数组,这里直接用np里面的方法来转数组 写到这里我突然就在想 为什么我不自己写 还要调用np里面的方法,我明明拥有行列数了,那么我可以直接对数据操作来得到一个二维数组。 归根到底还是我太懒了,下次一定不导入别人的库,争取自己完完整整的写
def my_skip(self, skip_lines=0, skip_rows = 0) -> None:
'''
通常用于第二列不为成绩的情况
默认不跳
'''
self.skip_lines = skip_lines
类里面的第五个方法是跳列 写这个的时候我也犹豫了下,因为本身read()方法里面是可以直接设置跳行跳列的,但是那样的话后面就不太好完成查找方法了,因为我想尽量保证数组的完整性,所以每次都是直接对原始数组进行操作,原始的数组没有保存,无法用原始数组进行查找,但是这样可以避免在数据过多的时候占用过多的储存空间
def my_total(self) -> str:
'''计算某同学的总分方法'''
self.total = sum(
self.array[self.need_row, self.skip_lines:].astype(int))
# 成绩从第几列开始 就从第几列开始算
return (f'{self.name}同学的总分为{self.total :.2f}')
def my_ave(self) -> str:
'''计算某同学的平均分'''
self.ave = self.total / (self.lines - self.skip_lines)
# 科目数计算为总行数减去跳过的行数
return (f'{self.name}同学的平均分为{self.ave :.2f}')
def sub_ave(self) -> str:
'''求单科班级平均分'''
ave_sub = np.mean(self.array[1:, self.need_line].astype(int))
# 从需要的列的第一行开始
return (f'{self.subject}课程平均成绩为{ave_sub:.2f}')
def sub_medium(self) -> str:
'''求单科班级中位数'''
medium_sub = np.median(self.array[1:, self.need_line].astype(int))
return (f'{self.subject}课程中位数为{medium_sub:.2f}')
def sub_std(self) -> str:
'''求单科班级标准差'''
std_sub = np.std(self.array[1:, self.need_line].astype(int))
return(f'{self.subject}课程标准差为{std_sub:.2f}')
类里面的最后几个方法就没有太大的难度了,本来也打算自己写的,但是既然都调np库了就直接用吧,反正也不难
最后测试
if __name__ == '__main__':
lines = int(input('请输入文件列数'))
rows = int(input('请输入文件行数'))
csv_name = input('请将文件置于同一文件夹并输入文件名(不带引号)') # 这里可以改成input
sc = scores(lines, rows) # 创建实例对象
sc.my_read(csv_name)
sc.my_find()
sc.my_array()
sc.my_skip(int(input('请输入成绩从第几列开始(索引数)')), int(input('请输入成绩从第几行开始(索引数)')))
print(sc.my_total())
print(sc.my_ave())
print(sc.sub_ave())
print(sc.sub_medium())
print(sc.sub_std())
最后输入一大堆的结果
最后如果要完成上面题目的话 记得要删除掉input()当中的字符和部分input()并对没有给出的参数进行提前赋值
总结: 手贱,再也不写类了,老老实实的面向过程吧