目录
12-第121节到第130节-代码训练.py的python文件:
好了,又一篇博客和代码写完了,励志一下吧,下一小节等等继续:
124节——数据分析案例——文件读取
1.学习目标
1.使用面向对象的思想完成数据的读取和处理
2.基于面向对象思想重新认知第三方库的使用(pyecharts)
2.数据分析案例的需求
3.数据内容
2011年1月的销售数据:每一条数据之间使用换行符进行分隔,每一条数据的其中的内容之间用逗号进行分隔,本条2011年1月的销售数据,他的每一条数据的具体内容的字段分别指的是——日期,订单id,销售额,销售省份。
这种由逗号分隔的文件,我们也可以把他叫做csv文件。
2011年2月的销售数据:每一条数据之间使用换行符进行分隔,每一条数据的其中的内容之间用逗号进行分隔,每一条数据是JSON格式的数据,本条2011年2月的销售数据,他的每一条数据的具体内容的字段分别指的是——日期,订单id,销售额,销售省份。
记住:我们做这个案例的需求是,求出每一份数据的总体的销售额!!!
例如:找到第二份JSON数据中的日期,好,确定好是“2月1日”,然后将这一份数据中的所有的销售额进行每一条的求和计算。第一份一月份的数据效果毅然是如此。
3.整体案例流程分析
1.读取数据
2.封装数据对象
3.计算数据对象
4.对所得结果内容进行pyecharts绘图
用面向对象的思想进行实战代码演练:无非是用到封装、继承、多态进行对数据的加工处理。
4.什么是csv文件
CSV 文件,简单说就是 “用逗号分隔数据的纯文本文件” ,核心特点:
1. 长啥样?(举例子)
比如表格里一行是:
日期 订单 ID 金额 省份 2011-01-01 4b34218c-9f37-4e66-b33e... 1689 湖南省 存成 CSV 就是 一行文字:
plaintext
2011-01-01,4b34218c-9f37-4e66-b33e-327ecd5fb897,1689,湖南省
2. 为啥用它?(好处)
- 简单到极致:就是普通文本,任何软件(Excel、记事本、代码)都能打开,不会像 Excel 文件那样因版本 / 格式出错。
- 方便处理:写代码时,读 CSV 比读 Excel 表格容易 10 倍(比如 Python 里几行代码就能解析)。
- 体积小:没有格式、公式,只存数据,传输 / 存储更省空间。
3. 和 Excel 有啥区别?
对比项 CSV 文件 Excel 文件(.xlsx) 本质 纯文本(能直接用记事本打开) 二进制格式(需软件解析) 能存的内容 只有数据,没格式 / 公式 能存格式、公式、图表等 处理难度 代码轻松读,通用性强 需专门库(如 openpyxl)处理 总结:
CSV 是 “表格数据的极简版文本形态” ,适合存简单数据(比如订单、统计结果),尤其方便编程处理或跨软件共享。如果只是记录 “行 + 列” 的纯数据,选 CSV 准没错!
编辑
分享
分享一些CSV文件的实际应用案例
如何将CSV文件导入到Excel中?
怎样使用Python处理CSV文件?
5.python中的readlines方法
Python 中的
readlines()
方法,是 文件对象的核心方法之一,专门用来 一次性读取文件的所有行,并把每行内容存成一个字符串,最终返回一个 列表(列表里每个元素对应文件的一行)。以下是最直白的解释:一、核心功能:把文件 “拆成一行行的列表”
比如你有个
test.txt
文件,内容是:plaintext
第一行 第二行 第三行
用
readlines()
读取后,会得到这样的列表:python
["第一行\n", "第二行\n", "第三行\n"]
- 每个元素是文件里的一行(包含末尾的换行符
\n
,比如第一行实际是"第一行\n"
);- 列表长度等于文件的行数。
二、怎么用?(代码示例)
python
# 1. 打开文件(建议用 with open,自动关文件) with open("test.txt", "r", encoding="utf-8") as f: # 2. 调用 readlines(),得到每行组成的列表 lines = f.readlines() # 3. 遍历列表,处理每行内容 for line in lines: # 注意:line 包含 \n,print 会自动换行,所以看起来会空一行? # 解决:用 line.strip() 去掉换行符和首尾空格,或 print(line, end='') print(line.strip()) # 输出:第一行 第二行 第三行(无多余换行)
三、关键细节(避坑必看)
“文件指针” 的影响:
文件里有个 “隐形指针”,记录当前读到哪里。如果调用readlines()
前,已经用read()
或readline()
读过内容,指针会往后移,readlines()
只会读指针后面的内容。比如:
python
with open("test.txt") as f: f.readline() # 读了第一行,指针移到第二行开头 lines = f.readlines() # 只会读第二行、第三行 print(lines) # 输出 ["第二行\n", "第三行\n"]
大文件别乱用!:
readlines()
会把整个文件一次性读进内存。如果文件是几个 G 的日志,直接撑爆内存!这时应该用readline()
逐行读,或分块处理。换行符的处理:
返回的每行都带\n
,比如打印时会自动换行。如果不想这样:
- 用
line.strip()
去掉所有首尾空白(包括\n
、空格);- 或
print(line, end='')
覆盖默认的换行。四、和其他方法的区别
方法 功能 返回值类型 内存占用特点 readlines
读所有行,存成列表 list
(每行是字符串)一次性占满文件大小的内存 readline
读一行,下次读下一行 str
(单行内容)每次只占一行内存,省内存 read
读整个文件成一个字符串 str
同样占满文件大小的内存 五、适合用的场景
- 处理小文件:比如配置文件、短日志,一次性读入方便处理;
- 需要随机访问行:把所有行存在列表里,随时通过索引取某一行(如
lines[3]
取第 4 行)。总结
readlines()
是 “一次性读取小文件所有行” 的快捷方式,适合简单场景;但遇到大文件要谨慎,换成逐行读取更安全。记住它的返回值是 “每行组成的列表”,处理时注意换行符即可~
readlines方法就是一次读取文件的全部内容,将文件中内容的每一行加上末尾的换行符作为一个元素,将这样的多个元素存到列表中,返回值的类型也是个列表。
6.代码开发
面向对象,数据分析案例,主业务逻辑代码 实现步骤: 1.设计一个类,可以完成数据的封装 2.设计一个抽象类,定义文件读取的相关功能,并使用子类实现具体功能 3.读取文件,生产数据对象 4.进行数据需求的逻辑计算(计算每一天的销售额) 5.通过pyecharts进行图形绘制
【1】文件读取的相关内容
12-第121节到第130节-代码训练.py
# 124节——数据分析步骤一
"""
面向对象,数据分析案例,主业务逻辑代码
实现步骤:
1.设计一个类,可以完成数据的封装
2.设计一个抽象类,定义文件读取的相关功能,并使用子类实现具体功能
3.读取文件,生产数据对象
4.进行数据需求的逻辑计算(计算每一天的销售额)
5.通过pyecharts进行图形绘制
"""
# 实现步骤二:设计一个抽象类,在抽象类的内部,完成定义文件读取的相关功能,并且使用具体的子类去实现读取相关的具体功能。
# 那么存在一个疑问: 这里为什么要使用抽象类?
# 答:通过分装、继承、多态等顶层设计思想,抽象类只定义文件读取的相关功能,但是并不实现,由后续的子类根据继承、多态、复写等方式进行相关功能的实现,完成了整体面向对象架构设计的实现。
file_define.py
# 和文件相关的类定义都在
import json
# 设计的读取文件的类有哪些功能,顶层设计,但是只设计,不实现
# 随后通过具体的子类,由封装、继承、多态具体实现具体的功能
# 然后比如说,实现文本读取的子类和JSON读取的子类来实现具体数据的具体读取方法,
# 这就是使用抽象类的原因。
# python中的抽象方法,就是包含抽象类的方法,而抽象类就是就是类中的成员方法由pass代替,没有具体的方法体
# 先定义一个抽象类用来做顶层设计,确定有哪些具体功能需要实现
from data_define import Record #这里是因为在标明抽象类的返回值是list类型的Record参数,而这个类中没有,所以要导包
# (简单理解,两个python文件如果想要实现功能上的联通,那么毫无疑问,必须导包啊!)
class FileReader:
def read_data(self)->list[Record]: #这里可以通过类型注解对返回值的类型进行一下注解
# 在Python类的实例方法中,self是必须写的第一个参数,核心原因是:它代表 “调用该方法的实例对象本身” ,让方法能访问和操作当前对象的属性 / 方法。
pass
"""读取文件的数据,将读到的每一条数据都转换为Record对象,将他们
都返回到list内返回"""
# 第一个,设计用于读取文本数据的文件读取器
# 继承上面的抽象类,具体的读取文件的功能实现,由继承的子类中利用多态的特性,具体实现
class TextFileReader(FileReader):
# 通过构造方法来定义成员变量
def __init__(self,path):
self.path=path #定义成员变量记录读取文件的具体路径,外部传入的path值,就可以给到成员变量本身
# 因为想要在成员方法之外读取成员变量,就必须使用self关键字
# 同时如果想要通过创建类对象就能实现成员变量的传参,就必须这样写self.path=path
# 复写(实现抽象方法)父类的方法
def read_data(self) ->list[Record]:
# 在Python类的实例方法中,self是必须写的第一个参数,核心原因是:它代表 “调用该方法的实例对象本身” ,让方法能访问和操作当前对象的属性 / 方法。
# 读取具体的文件
#参数为:文件的路径、模式和固定的encoding="utf-8"的文件编码的标准
f=open(self.path,"r",encoding="utf-8")
# 通过for循环对于文件当中的每一行进行进行具体的访问
# readlines方法就是一次读取文件的全部内容,将文件中内容的每一行加上末尾的换行符作为一个元素,将这样的多个元素存到列表中,返回值的类型也是个列表。
record_list:list(Record)=[] #并且标明好类型注解record_list这个空列表的其中的元素的内容是列表类型内容是Record的类对象:list里面存放的就是Record这个类对象
for line in f.readlines():
# 消除读取到的数据的每一行的换行符
line=line.strip()
# 开始通过split方法进行每一行数据间的分割。每一行数据去掉了末尾的换行符,然后通过了split方法进行逗号的切分,到这里数据差不多就封装好了。
data_list=line.split(",")
# 然后把封装好的data_list数据转变成Record类对象
# 还要把金额的字符串类型转换为整数类型
record=Record(data_list[0],data_list[1],int(data_list[2]),data_list[3])
# print(line)
# 这里的第一步,就是要把readlines的返回值类型为列表的每一个元素末尾的换行符去掉
# 把record切分好并且封装好的每一个元素通过append方法返回给record_list这个列表中去
record_list.append(record)
# 注意:open之后,必须要有close关闭掉,否则会一直占用这个文件
f.close()
# 将列表record_list做返回值,在read_data这个方法中的类型注解也标明了返回值是列表类型的Record类对象。
return record_list
class JsonFileReader(FileReader):
# 定义成员变量记录文件的路径
def __init__(self,path):
self.path=path
def read_data(self) ->list[Record]:
# 先是读取数据,用open的方式
f=open(self.path,"r",encoding="utf-8")
# json格式的数据,用方法转换成字典的类型的数据,然后再返回为列表的类型。我们对于JSON格式数据的处理与上面的csv格式的数据的处理方式不同。
record_list:list[Record]=[]
# 用for循环和readlines方法循环处理JSON格式的数据
for line in f.readlines():
# 通过loads这个方法,将JSON格式的数据转换为字典类型的数据
data_dict=json.loads(line)
# 通过创建Record类对象并且传参(键值对类型的参数)进行数据的处理
record=Record(data_dict["date"],data_dict["order_id"],int(data_dict["money"]),data_dict["province"])
# 然后通过append方法,将处理好的record数据,放入record_list这个列表中
record_list.append(record)
# 最后要close关闭文件,还要将处理好的格式为列表的数据作为返回值返回
f.close()
return record_list
# 通过main方法进行测试,确保右键运行的时候可以测试,但是导包导进来的内容不会被执行,说人话就是进行安全模式的测试
# 这个是csv文件的测试方法
# if __name__ == '__main__':
# text_file_reader=TextFileReader("D:/2011年1月的销售数据.txt") #进行传入文件路径创建了一个类对象
# list1=text_file_reader.read_data() #通过类对象来调用读取文件的方法,看看控制台输出到底是什么样子
# for l in list1:
# print(l)
# # 2011 - 01 - 01, 4
# # b34218c - 9
# # f37 - 4e66 - b33e - 327
# # ecd5fb897, 1689, 湖南省
# #
# # 2011 - 01 - 01, 5
# # b6a6417 - 9
# # a16 - 4243 - 9704 - 255719074
# # bff, 2353, 河北省
# #
# # 2011 - 01 - 01, ae240260 - 68
# # a9 - 4e59 - b4c9 - 206
# # be4c08a8d, 2565, 湖北省
# # 这样的就是末尾有一个换行符没有去掉
#
# # 通过strip方法,把每一行数据末尾的换行符去掉以后,得到的数据打印结果进行部分展示:
# # 2011 - 01 - 01, 4
# # b34218c - 9
# # f37 - 4e66 - b33e - 327
# # ecd5fb897, 1689, 湖南省
# # 2011 - 01 - 01, 5
# # b6a6417 - 9
# # a16 - 4243 - 9704 - 255719074
# # bff, 2353, 河北省
#
#
# # print("-----------------分隔线----------")
#
#
# # 现在再来一个JSON文件的测试方法
# if __name__ == '__main__':
# # 创建类对象并传入路径这个参数,然后再通过类对象进行read_data方法的调用以用来测试数据是否已经被精准的处理好
# json_file_reader=JsonFileReader("D:/2011年2月的销售数据JSON.txt")
# list2=json_file_reader.read_data()
# for l in list2:
# print(l)
if __name__ == '__main__':
text_file_reader = TextFileReader("D:/2011年1月的销售数据.txt")
json_file_reader = JsonFileReader("D:/2011年2月的销售数据JSON.txt")
list1=text_file_reader.read_data()
list2=json_file_reader.read_data()
for l in list1:
print(l)
for l in list2:
print(l)
# 控制台输出结果:
# 2011-01-01,4b34218c-9f37-4e66-b33e-327ecd5fb897,1689,湖南省
# 2011-01-01,5b6a6417-9a16-4243-9704-255719074bff,2353,河北省
# 2011-01-01,ae240260-68a9-4e59-b4c9-206be4c08a8d,2565,湖北省
# 2011-01-01,c833e851-880f-4e05-9de5-b547f5ffc5e1,2877,山东省
# 2011-01-01,dd27e822-884c-4d20-a309-986f6a90e2b9,947,安徽省
# 2011-01-01,8e43e3c5-44bf-4219-8a59-f512607aeeefe,815,河北省
# 2011-01-01,b682f5f-fb10-4210-9e45-288dd2239594,1363,广东省
# 2011-01-01,fd5056a8-8223-4d02-9988-04e1b41a57e8,2149,江苏省
# 2011-01-01,d022df35-3c0e-4753-bccb-37e125a5922b,1739,福建省
# 2011-01-01,a480686a-77ff-497e-9e32-0f6d9ba3eadd,2999,江苏省
# 2011-02-01,caf99222-53a6-427b-925d-3187fc71a86a,1805,江西省
# 2011-02-01,3dea6f83-a9b2-4197-ba9f-2b25704c530b,2547,广东省
# 2011-02-01,952733e-3900-4d9f-a706-81330c1ddbb,1216,福建省
# 2011-02-01,00ccfa25-e555-a464-ad7b-e238ca3aabe7,1193,四川省
# 2011-02-01,06fee745-ac6e-4843-968b-bf7847cfdbf7,2370,云南省
# 2011-02-01,0be12f8-fce5-426f-9856-9163864c5f1a,230,江西省
# 2011-02-01,2f629b33-62a5-44c1-ba0f-b983e81fb4d0,2291,福建省
# 2011-02-01,723d1750-f81d-4164-8127-b57c0d8f970c,2453,江苏省
# 2011-02-01,7eda2c9-fe89-403d-84cb-b6f47381c89b,2637,河南省
# 2011-02-01,41d9f2ca-88b8-4bb7-b64b-5c5d9ba54168,2321,贵州省
data_define.py
"""
数据定义的类
"""
# 实现步骤一:1.设计一个类,可以完成数据的封装
class Record:
# 定义数据的相应字段
# 方式一:
# 【字段包括:日期、订单id、销售额、销售省份】
# date=None
# order_id=None
# money=None
# province=None
# 方式二:通过构造方法完成,对成员变量的定义,此法,可以在创建类对象的时候,给成员变量进行直接赋值,方便高效
# 【字段包括:日期、订单id、销售额、销售省份】
def __init__(self,date,order_id,money,province):
# 如果在成员方法外,使用定义的成员变量,那么必须要在成员变量之前加上self关键字
# 对于在构造方法中的成员变量,他的值就不是None,而是在形参中定义到的,在以后实参传给形参的具体的值
self.date=date #订单日期
self.order_id=order_id #订单ID
self.money=money #订单金额
self.province=province #销售省份
# 好了,至此,我们完成了一个基础的类的定义,这个类是Record,用来记录数据的基本信息。
# 这里没有成员方法,等后续需要成员方法的时候再补上。
# 通过__str__的魔术方法,把运行结果像这样的<data_define.Record object at 0x000002036B212390>内存地址转换为正常的内容进行输出
def __str__(self):
return f"{self.date},{self.order_id},{self.money},{self.province}"
# 运行结果:
125节——数据分析案例——数据计算
1.代码的逻辑计算
OK,在上述内容里面完成了两个csv文件、JSON文件的数据读取操作,那现在,我们返回主python文件中,开始进行此项目操作的具体逻辑计算:
12-第121节到第130节-代码训练.py:
# 124节——数据分析步骤一
"""
面向对象,数据分析案例,主业务逻辑代码
实现步骤:
1.设计一个类,可以完成数据的封装
2.设计一个抽象类,定义文件读取的相关功能,并使用子类实现具体功能
3.读取文件,生产数据对象
4.进行数据需求的逻辑计算(计算每一天的销售额)
5.通过pyecharts进行图形绘制
"""
# 实现步骤二:设计一个抽象类,在抽象类的内部,完成定义文件读取的相关功能,并且使用具体的子类去实现读取相关的具体功能。
# 那么存在一个疑问: 这里为什么要使用抽象类?
# 答:通过分装、继承、多态等顶层设计思想,抽象类只定义文件读取的相关功能,但是并不实现,由后续的子类根据继承、多态、复写等方式进行相关功能的实现,完成了整体面向对象架构设计的实现。
# 先导包,因为我们使用到了python面向对象的继承、分装、多态等特性,所以为了实现“梦幻联动”的效果,我们要导包(这个导包是实现返回值为列表且处理好的数据的导包)
from file_define import FileReader,TextFileReader,JsonFileReader
# 实现Record数据的封装类的导包
from data_define import Record
# 这里来获取csv和JSON文本数据 【TextFileReader】 【JsonFileReader】 【通过创建类对象并传入文件位置路径的参数】
text_file_reader=TextFileReader("D:/2011年1月的销售数据.txt")
json_file_reader=JsonFileReader("D:/2011年2月的销售数据JSON.txt")
# 可以通过类对象.read_data方法进行处理好的数据的访问(获取好处理好的数据),并且为了看着明白,再写好类型注解
jan_data:list[Record]=text_file_reader.read_data() #表示一月份的数据
feb_data:list[Record]=json_file_reader.read_data() #表示二月份的数据
# 将两个月份的数据合并到一个list进行存储
all_data:list[Record]=jan_data+feb_data
# 思路:
# 进行数据计算 ---> 需求:计算每个月的销售额
# 若是要计算1月份的销售额,那么就拿到1月份的日期,然后吧1月份日期对应的money字段的值进行一个累加即可【同理可得,二月份也一样】
# 使用字典类型来处理一月份或者是二月份的日期的字段的数据,如:{"2011-01-01":"1234"}这样的日期为键,销售额为值的键值对形式方便对数据的日期和销售额进行处理【相同月份的日期数据,他的销售额进行循环累加】
# 因为字典的key不会重复,所以用key来当做日期字段,进行不同月份的的money的循环累加,就可以实现最终的需求——查看每个月的销售额分别是多少
# 先定义一个空字典,方便我们在实现上述思路
data_dict={}
for record in all_data:
# 进行if判断日期
if record.date in data_dict.keys():
#pass #if-else判断,一开始data_dict是空字典,所以pass跳过就好。
# 并且后续里面有了数据记录,那么和老数据记录进行累加即可
# 如:{"2011-01-01": "1234"},字典的date键相同,数据的money字段进行累加
data_dict[record.date]+=record.money
else:
# 第一条从没见过的日期,将他的金额的值,赋值给日期的键
data_dict[record.date]=record.money
# 进行运行检查结果
print(data_dict)
# {'2011-01-01': 19496, '2011-02-01': 19063}
# !!!注意!!!:
# {'2011-01-01': 19496, '2011-02-01': 19063}
# 因为我的原始数据只有:2011-01-01和2011-02-01的这两个日期的数据,所以结果就没有:2011-01-02、2011-01-03等记录。
# 因为原数据的csv文件和JSON文件的数据都是我截图保留了一部分,如果有1、2月份的其他日期的可视化时的需要,那么我明天的博客里面再将原数据增添更改一下
126节——数据分析案例——可视化
1.代码实现
12-第121节到第130节-代码训练.py的python文件:
# 124节——数据分析步骤一
"""
面向对象,数据分析案例,主业务逻辑代码
实现步骤:
1.设计一个类,可以完成数据的封装
2.设计一个抽象类,定义文件读取的相关功能,并使用子类实现具体功能
3.读取文件,生产数据对象
4.进行数据需求的逻辑计算(计算每一天的销售额)
5.通过pyecharts进行图形绘制
"""
# 实现步骤二:设计一个抽象类,在抽象类的内部,完成定义文件读取的相关功能,并且使用具体的子类去实现读取相关的具体功能。
# 那么存在一个疑问: 这里为什么要使用抽象类?
# 答:通过分装、继承、多态等顶层设计思想,抽象类只定义文件读取的相关功能,但是并不实现,由后续的子类根据继承、多态、复写等方式进行相关功能的实现,完成了整体面向对象架构设计的实现。
# 先导包,因为我们使用到了python面向对象的继承、分装、多态等特性,所以为了实现“梦幻联动”的效果,我们要导包(这个导包是实现返回值为列表且处理好的数据的导包)
from file_define import FileReader,TextFileReader,JsonFileReader
# 实现Record数据的封装类的导包
from data_define import Record
# 现在导可视化的包
# 导入Bar这个包,通过导入Bar这个包来实现构建一个柱状图
from pyecharts.charts import Bar
# 把pyecharts中的做可视化图表所用到的所有可以用到的选项都导入
from pyecharts.options import *
# 通过global导入主题类型的包:可以控制颜色
from pyecharts.globals import ThemeType
# 这里来获取csv和JSON文本数据 【TextFileReader】 【JsonFileReader】 【通过创建类对象并传入文件位置路径的参数】
text_file_reader=TextFileReader("D:/2011年1月的销售数据.txt")
json_file_reader=JsonFileReader("D:/2011年2月的销售数据JSON.txt")
# 可以通过类对象.read_data方法进行处理好的数据的访问(获取好处理好的数据),并且为了看着明白,再写好类型注解
jan_data:list[Record]=text_file_reader.read_data() #表示一月份的数据
feb_data:list[Record]=json_file_reader.read_data() #表示二月份的数据
# 将两个月份的数据合并到一个list进行存储
all_data:list[Record]=jan_data+feb_data
# 思路:
# 进行数据计算 ---> 需求:计算每个月的销售额
# 若是要计算1月份的销售额,那么就拿到1月份的日期,然后吧1月份日期对应的money字段的值进行一个累加即可【同理可得,二月份也一样】
# 使用字典类型来处理一月份或者是二月份的日期的字段的数据,如:{"2011-01-01":"1234"}这样的日期为键,销售额为值的键值对形式方便对数据的日期和销售额进行处理【相同月份的日期数据,他的销售额进行循环累加】
# 因为字典的key不会重复,所以用key来当做日期字段,进行不同月份的的money的循环累加,就可以实现最终的需求——查看每个月的销售额分别是多少
# 先定义一个空字典,方便我们在实现上述思路
data_dict={}
for record in all_data:
# 进行if判断日期
if record.date in data_dict.keys():
#pass #if-else判断,一开始data_dict是空字典,所以pass跳过就好。
# 并且后续里面有了数据记录,那么和老数据记录进行累加即可
# 如:{"2011-01-01": "1234"},字典的date键相同,数据的money字段进行累加
data_dict[record.date]+=record.money
else:
# 第一条从没见过的日期,将他的金额的值,赋值给日期的键
data_dict[record.date]=record.money
# 进行运行检查结果
print(data_dict)
# {'2011-01-01': 19496, '2011-02-01': 19063}
# !!!注意!!!:
# {'2011-01-01': 19496, '2011-02-01': 19063}
# 因为我的原始数据只有:2011-01-01和2011-02-01的这两个日期的数据,所以结果就没有:2011-01-02、2011-01-03等记录。
# 因为原数据的csv文件和JSON文件的数据都是我截图保留了一部分,如果有1、2月份的其他日期的可视化时的需要,那么我明天的博客里面再将原数据增添更改一下
# 127节——案例练习的数据可视化部分
# 我们在上述得到的data_dict这个字典中,key是日期,value是销售额。
# 我们在这里使用pyecharts模块进行可视化开发。
# 现在开始进行可视化图表的开发:
# 通过Bar()这个类,来创建一个类对象
# 通过对已经写好的构造方法进行传参,对主题进行设置
bar=Bar(init_opts=InitOpts(theme=ThemeType.LIGHT))
# 现在通过类对象的各种参数来配置可视化图表的各个参数和模块:
# 这里对x轴进行设置:
# 我们在上述得到的data_dict这个字典中,key是日期,value是销售额。
# 而传入的参数时,我们不能将data_dict.keys()这样的字典的键直接传入(这样会出现warning),而是要先转换为列表的类型(warning就消失了)。
bar.add_xaxis(list(data_dict.keys())) #添加x轴的数据
# 这里对y轴进行设置:
bar.add_yaxis("销售额",list(data_dict.values()),label_opts=LabelOpts(is_show=False)) #label_opts=LabelOpts(is_show=False):这是不显示label。
#添加了y轴的数据:名称、数据来源,创建LabelOpts这个对象来进行柱状图的每个条所标明的具体值进行隐藏操作 (这是已经写好的构造方法,可以直接进行传参的操作)。
# 对可视化图表进行设置标题:
bar.set_global_opts(
# 通过构建TitleOpts这个类对象,来进行参数的设置。
# 通过构造方法的形式(TitleOpts这个内置的构造方法已经被写好了,所以进行直接创建类对象和传参就好了),在对类对象用具体参数进行传参的时候,直接给其传入数据。
title_opts=TitleOpts(title="每日销售额") #设置柱状图的标题
)
# 对运行产生的HTML网页的可视化图表的柱状图的HTML文件进行起个别名
bar.render("每日销售柱状图.html")
每日销售柱状图.py的HTML文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Awesome-pyecharts</title>
<script type="text/javascript" src="https://assets.pyecharts.org/assets/v5/echarts.min.js"></script>
</head>
<body >
<div id="65b788591cfc4517aac7095cf293b22a" class="chart-container" style="width:900px; height:500px; "></div>
<script>
var chart_65b788591cfc4517aac7095cf293b22a = echarts.init(
document.getElementById('65b788591cfc4517aac7095cf293b22a'), 'light', {renderer: 'canvas'});
var option_65b788591cfc4517aac7095cf293b22a = {
"animation": true,
"animationThreshold": 2000,
"animationDuration": 1000,
"animationEasing": "cubicOut",
"animationDelay": 0,
"animationDurationUpdate": 300,
"animationEasingUpdate": "cubicOut",
"animationDelayUpdate": 0,
"aria": {
"enabled": false
},
"series": [
{
"type": "bar",
"name": "\u9500\u552e\u989d",
"legendHoverLink": true,
"data": [
19496,
19063
],
"realtimeSort": false,
"showBackground": false,
"stackStrategy": "samesign",
"cursor": "pointer",
"barMinHeight": 0,
"barCategoryGap": "20%",
"barGap": "30%",
"large": false,
"largeThreshold": 400,
"seriesLayoutBy": "column",
"datasetIndex": 0,
"clip": true,
"zlevel": 0,
"z": 2,
"label": {
"show": false,
"margin": 8,
"valueAnimation": false
}
}
],
"legend": [
{
"data": [
"\u9500\u552e\u989d"
],
"selected": {},
"show": true,
"padding": 5,
"itemGap": 10,
"itemWidth": 25,
"itemHeight": 14,
"backgroundColor": "transparent",
"borderColor": "#ccc",
"borderRadius": 0,
"pageButtonItemGap": 5,
"pageButtonPosition": "end",
"pageFormatter": "{current}/{total}",
"pageIconColor": "#2f4554",
"pageIconInactiveColor": "#aaa",
"pageIconSize": 15,
"animationDurationUpdate": 800,
"selector": false,
"selectorPosition": "auto",
"selectorItemGap": 7,
"selectorButtonGap": 10
}
],
"tooltip": {
"show": true,
"trigger": "item",
"triggerOn": "mousemove|click",
"axisPointer": {
"type": "line"
},
"showContent": true,
"alwaysShowContent": false,
"showDelay": 0,
"hideDelay": 100,
"enterable": false,
"confine": false,
"appendToBody": false,
"transitionDuration": 0.4,
"textStyle": {
"fontSize": 14
},
"borderWidth": 0,
"padding": 5,
"order": "seriesAsc"
},
"xAxis": [
{
"show": true,
"scale": false,
"nameLocation": "end",
"nameGap": 15,
"gridIndex": 0,
"inverse": false,
"offset": 0,
"splitNumber": 5,
"minInterval": 0,
"splitLine": {
"show": true,
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
}
},
"animation": true,
"animationThreshold": 2000,
"animationDuration": 1000,
"animationEasing": "cubicOut",
"animationDelay": 0,
"animationDurationUpdate": 300,
"animationEasingUpdate": "cubicOut",
"animationDelayUpdate": 0,
"data": [
"2011-01-01",
"2011-02-01"
]
}
],
"yAxis": [
{
"show": true,
"scale": false,
"nameLocation": "end",
"nameGap": 15,
"gridIndex": 0,
"inverse": false,
"offset": 0,
"splitNumber": 5,
"minInterval": 0,
"splitLine": {
"show": true,
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
}
},
"animation": true,
"animationThreshold": 2000,
"animationDuration": 1000,
"animationEasing": "cubicOut",
"animationDelay": 0,
"animationDurationUpdate": 300,
"animationEasingUpdate": "cubicOut",
"animationDelayUpdate": 0
}
],
"title": [
{
"show": true,
"text": "\u6bcf\u65e5\u9500\u552e\u989d",
"target": "blank",
"subtarget": "blank",
"padding": 5,
"itemGap": 10,
"textAlign": "auto",
"textVerticalAlign": "auto",
"triggerEvent": false
}
]
};
chart_65b788591cfc4517aac7095cf293b22a.setOption(option_65b788591cfc4517aac7095cf293b22a);
</script>
</body>
</html>
file_define.py的python代码文件:
# 和文件相关的类定义都在
import json
# 设计的读取文件的类有哪些功能,顶层设计,但是只设计,不实现
# 随后通过具体的子类,由封装、继承、多态具体实现具体的功能
# 然后比如说,实现文本读取的子类和JSON读取的子类来实现具体数据的具体读取方法,
# 这就是使用抽象类的原因。
# python中的抽象方法,就是包含抽象类的方法,而抽象类就是就是类中的成员方法由pass代替,没有具体的方法体
# 先定义一个抽象类用来做顶层设计,确定有哪些具体功能需要实现
from data_define import Record #这里是因为在标明抽象类的返回值是list类型的Record参数,而这个类中没有,所以要导包
# (简单理解,两个python文件如果想要实现功能上的联通,那么毫无疑问,必须导包啊!)
class FileReader:
def read_data(self)->list[Record]: #这里可以通过类型注解对返回值的类型进行一下注解
# 在Python类的实例方法中,self是必须写的第一个参数,核心原因是:它代表 “调用该方法的实例对象本身” ,让方法能访问和操作当前对象的属性 / 方法。
pass
"""读取文件的数据,将读到的每一条数据都转换为Record对象,将他们
都返回到list内返回"""
# 第一个,设计用于读取文本数据的文件读取器
# 继承上面的抽象类,具体的读取文件的功能实现,由继承的子类中利用多态的特性,具体实现
class TextFileReader(FileReader):
# 通过构造方法来定义成员变量
def __init__(self,path):
self.path=path #定义成员变量记录读取文件的具体路径,外部传入的path值,就可以给到成员变量本身
# 因为想要在成员方法之外读取成员变量,就必须使用self关键字
# 同时如果想要通过创建类对象就能实现成员变量的传参,就必须这样写self.path=path
# 复写(实现抽象方法)父类的方法
def read_data(self) ->list[Record]:
# 在Python类的实例方法中,self是必须写的第一个参数,核心原因是:它代表 “调用该方法的实例对象本身” ,让方法能访问和操作当前对象的属性 / 方法。
# 读取具体的文件
#参数为:文件的路径、模式和固定的encoding="utf-8"的文件编码的标准
f=open(self.path,"r",encoding="utf-8")
# 通过for循环对于文件当中的每一行进行进行具体的访问
# readlines方法就是一次读取文件的全部内容,将文件中内容的每一行加上末尾的换行符作为一个元素,将这样的多个元素存到列表中,返回值的类型也是个列表。
record_list:list(Record)=[] #并且标明好类型注解record_list这个空列表的其中的元素的内容是列表类型内容是Record的类对象:list里面存放的就是Record这个类对象
for line in f.readlines():
# 消除读取到的数据的每一行的换行符
line=line.strip()
# 开始通过split方法进行每一行数据间的分割。每一行数据去掉了末尾的换行符,然后通过了split方法进行逗号的切分,到这里数据差不多就封装好了。
data_list=line.split(",")
# 然后把封装好的data_list数据转变成Record类对象
# 还要把金额的字符串类型转换为整数类型
record=Record(data_list[0],data_list[1],int(data_list[2]),data_list[3])
# print(line)
# 这里的第一步,就是要把readlines的返回值类型为列表的每一个元素末尾的换行符去掉
# 把record切分好并且封装好的每一个元素通过append方法返回给record_list这个列表中去
record_list.append(record)
# 注意:open之后,必须要有close关闭掉,否则会一直占用这个文件
f.close()
# 将列表record_list做返回值,在read_data这个方法中的类型注解也标明了返回值是列表类型的Record类对象。
return record_list
class JsonFileReader(FileReader):
# 定义成员变量记录文件的路径
def __init__(self,path):
self.path=path
def read_data(self) ->list[Record]:
# 先是读取数据,用open的方式
f=open(self.path,"r",encoding="utf-8")
# json格式的数据,用方法转换成字典的类型的数据,然后再返回为列表的类型。我们对于JSON格式数据的处理与上面的csv格式的数据的处理方式不同。
record_list:list[Record]=[]
# 用for循环和readlines方法循环处理JSON格式的数据
for line in f.readlines():
# 通过loads这个方法,将JSON格式的数据转换为字典类型的数据
data_dict=json.loads(line)
# 通过创建Record类对象并且传参(键值对类型的参数)进行数据的处理
record=Record(data_dict["date"],data_dict["order_id"],int(data_dict["money"]),data_dict["province"])
# 然后通过append方法,将处理好的record数据,放入record_list这个列表中
record_list.append(record)
# 最后要close关闭文件,还要将处理好的格式为列表的数据作为返回值返回
f.close()
return record_list
# 通过main方法进行测试,确保右键运行的时候可以测试,但是导包导进来的内容不会被执行,说人话就是进行安全模式的测试
# 这个是csv文件的测试方法
# if __name__ == '__main__':
# text_file_reader=TextFileReader("D:/2011年1月的销售数据.txt") #进行传入文件路径创建了一个类对象
# list1=text_file_reader.read_data() #通过类对象来调用读取文件的方法,看看控制台输出到底是什么样子
# for l in list1:
# print(l)
# # 2011 - 01 - 01, 4
# # b34218c - 9
# # f37 - 4e66 - b33e - 327
# # ecd5fb897, 1689, 湖南省
# #
# # 2011 - 01 - 01, 5
# # b6a6417 - 9
# # a16 - 4243 - 9704 - 255719074
# # bff, 2353, 河北省
# #
# # 2011 - 01 - 01, ae240260 - 68
# # a9 - 4e59 - b4c9 - 206
# # be4c08a8d, 2565, 湖北省
# # 这样的就是末尾有一个换行符没有去掉
#
# # 通过strip方法,把每一行数据末尾的换行符去掉以后,得到的数据打印结果进行部分展示:
# # 2011 - 01 - 01, 4
# # b34218c - 9
# # f37 - 4e66 - b33e - 327
# # ecd5fb897, 1689, 湖南省
# # 2011 - 01 - 01, 5
# # b6a6417 - 9
# # a16 - 4243 - 9704 - 255719074
# # bff, 2353, 河北省
#
#
# # print("-----------------分隔线----------")
#
#
# # 现在再来一个JSON文件的测试方法
# if __name__ == '__main__':
# # 创建类对象并传入路径这个参数,然后再通过类对象进行read_data方法的调用以用来测试数据是否已经被精准的处理好
# json_file_reader=JsonFileReader("D:/2011年2月的销售数据JSON.txt")
# list2=json_file_reader.read_data()
# for l in list2:
# print(l)
if __name__ == '__main__':
text_file_reader = TextFileReader("D:/2011年1月的销售数据.txt")
json_file_reader = JsonFileReader("D:/2011年2月的销售数据JSON.txt")
list1=text_file_reader.read_data()
list2=json_file_reader.read_data()
for l in list1:
print(l)
for l in list2:
print(l)
# 控制台输出结果:
# 2011-01-01,4b34218c-9f37-4e66-b33e-327ecd5fb897,1689,湖南省
# 2011-01-01,5b6a6417-9a16-4243-9704-255719074bff,2353,河北省
# 2011-01-01,ae240260-68a9-4e59-b4c9-206be4c08a8d,2565,湖北省
# 2011-01-01,c833e851-880f-4e05-9de5-b547f5ffc5e1,2877,山东省
# 2011-01-01,dd27e822-884c-4d20-a309-986f6a90e2b9,947,安徽省
# 2011-01-01,8e43e3c5-44bf-4219-8a59-f512607aeeefe,815,河北省
# 2011-01-01,b682f5f-fb10-4210-9e45-288dd2239594,1363,广东省
# 2011-01-01,fd5056a8-8223-4d02-9988-04e1b41a57e8,2149,江苏省
# 2011-01-01,d022df35-3c0e-4753-bccb-37e125a5922b,1739,福建省
# 2011-01-01,a480686a-77ff-497e-9e32-0f6d9ba3eadd,2999,江苏省
# 2011-02-01,caf99222-53a6-427b-925d-3187fc71a86a,1805,江西省
# 2011-02-01,3dea6f83-a9b2-4197-ba9f-2b25704c530b,2547,广东省
# 2011-02-01,952733e-3900-4d9f-a706-81330c1ddbb,1216,福建省
# 2011-02-01,00ccfa25-e555-a464-ad7b-e238ca3aabe7,1193,四川省
# 2011-02-01,06fee745-ac6e-4843-968b-bf7847cfdbf7,2370,云南省
# 2011-02-01,0be12f8-fce5-426f-9856-9163864c5f1a,230,江西省
# 2011-02-01,2f629b33-62a5-44c1-ba0f-b983e81fb4d0,2291,福建省
# 2011-02-01,723d1750-f81d-4164-8127-b57c0d8f970c,2453,江苏省
# 2011-02-01,7eda2c9-fe89-403d-84cb-b6f47381c89b,2637,河南省
# 2011-02-01,41d9f2ca-88b8-4bb7-b64b-5c5d9ba54168,2321,贵州省
data_define.py的python文件:
"""
数据定义的类
"""
# 实现步骤一:1.设计一个类,可以完成数据的封装
class Record:
# 定义数据的相应字段
# 方式一:
# 【字段包括:日期、订单id、销售额、销售省份】
# date=None
# order_id=None
# money=None
# province=None
# 方式二:通过构造方法完成,对成员变量的定义,此法,可以在创建类对象的时候,给成员变量进行直接赋值,方便高效
# 【字段包括:日期、订单id、销售额、销售省份】
def __init__(self,date,order_id,money,province):
# 如果在成员方法外,使用定义的成员变量,那么必须要在成员变量之前加上self关键字
# 对于在构造方法中的成员变量,他的值就不是None,而是在形参中定义到的,在以后实参传给形参的具体的值
self.date=date #订单日期
self.order_id=order_id #订单ID
self.money=money #订单金额
self.province=province #销售省份
# 好了,至此,我们完成了一个基础的类的定义,这个类是Record,用来记录数据的基本信息。
# 这里没有成员方法,等后续需要成员方法的时候再补上。
# 通过__str__的魔术方法,把运行结果像这样的<data_define.Record object at 0x000002036B212390>内存地址转换为正常的内容进行输出
def __str__(self):
return f"{self.date},{self.order_id},{self.money},{self.province}"
# 运行结果:
通过抽象类做顶层设计,具体的实现在子类中实现。
通过类进行成员变量或成员方法的封装。
还可以通过魔术方法进行成员变量的个性化的设置。
通过导包和导入第三方库,实现互相的封装、继承、多态,然后读取数据、进行数据的处理计算、以及可视化图表的具体开发,完成了一个整体的数据分析的案例实战!!!
2.具体的可视化图表的实现
好了,又一篇博客和代码写完了,励志一下吧,下一小节等等继续:
Patrick:
当你敲下最后一行代码注释时,当你在健身房把最后一组动作推到力竭时,你已经在书写自己的《洛奇》剧本了 —— 那些别人看来 “低效” 的 12 小时,那些汗流浃背的 3 小时,从来不是浪费,而是让 “nobody” 变成 “somebody” 的必经之路。
你说自己 “迟钝”,但我看到的是:1 小时的视频内容,你用一周拆解出代码背后的逻辑,给每个变量、每个方法写满注释 —— 这不是慢,是像工匠打磨铁器一样,把知识的棱角都捏合进自己的认知里。面向对象的抽象、继承的复用、多态的灵活,本就是需要在 “困惑 - 试错 - 顿悟” 里反复浸泡的概念,你愿意花时间和它们 “死磕”,这种对细节的执着,恰恰是程序员最珍贵的特质。就像你写
Record
类封装数据时,那些对self
关键字的追问、对抽象类意义的琢磨,早已悄悄让 “恐惧” 变成了 “掌控”。至于健身和身体的挑战 —— 每天 5 公里跑步、1.5 小时力量训练,还要对抗random tremble带来的颤抖与疲劳,这本身就是比任何代码都硬核的 “循环结构”。大卫・戈金斯说 “痛苦是礼物”,你比谁都懂这句话的重量:recover的副作用、random tremble的隐忧,这些是生活给你的 “异常值”,但你用 “每日训练” 的
while True
循环,硬是把它们变成了 “韧性” 的参数。你怕random tremble的复发?可你每天站在健身房里的样子,早已证明:意志的稳定,能对冲身体的不稳定。别觉得 “坚持” 是理所当然。那些因疲劳拖沓的日子,那些担心 “做不到” 的瞬间,恰恰是你和自己的懦弱 “对练” 的时刻 —— 就像洛奇在费城街头晨跑,不是为了打败阿波罗,是为了告诉自己 “我能比昨天多跑一个街区”。你现在做的,无论是一行行调试代码,还是一圈圈完成 5 公里,都是在给 “Patrick” 这个类,添加 “永不退出” 的方法。
把这封信存在博客里吧。下次觉得累了,看看它:你曾在代码里学会 “封装” 疲惫,在汗水中 “继承” 坚韧,在与random tremble的周旋中 “多态” 地活着 —— 这本身,就是比任何证书、任何身材都耀眼的成就。
继续做自己的拳王,Patrick。铃声还没响,回合还在继续。
一个懂你 “死磕” 的朋友
2025 年 7 月 12 日