阶段二开始-第一章—8天Python从入门到精通【itheima】-124节+125节+126节(数据分析案例一——文件读取+数据计算+可视化)

目录

124节——数据分析案例——文件读取

1.学习目标

2.数据分析案例的需求

3.数据内容

3.整体案例流程分析

4.什么是csv文件

1. 长啥样?(举例子)

2. 为啥用它?(好处)

3. 和 Excel 有啥区别?

总结:

5.python中的readlines方法

一、核心功能:把文件 “拆成一行行的列表”

二、怎么用?(代码示例)

三、关键细节(避坑必看)

四、和其他方法的区别

五、适合用的场景

总结

6.代码开发

【1】文件读取的相关内容

12-第121节到第130节-代码训练.py

file_define.py

data_define.py

125节——数据分析案例——数据计算

1.代码的逻辑计算

12-第121节到第130节-代码训练.py:

126节——数据分析案例——可视化

1.代码实现

12-第121节到第130节-代码训练.py的python文件:

每日销售柱状图.py的HTML文件:

file_define.py的python代码文件:

data_define.py的python文件:

2.具体的可视化图表的实现

好了,又一篇博客和代码写完了,励志一下吧,下一小节等等继续:


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-014b34218c-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())  # 输出:第一行  第二行  第三行(无多余换行)  

三、关键细节(避坑必看)

  1. “文件指针” 的影响
    文件里有个 “隐形指针”,记录当前读到哪里。如果调用 readlines() 前,已经用 read() 或 readline() 读过内容,指针会往后移,readlines() 只会读指针后面的内容

    比如:

    python

    with open("test.txt") as f:  
        f.readline()  # 读了第一行,指针移到第二行开头  
        lines = f.readlines()  # 只会读第二行、第三行  
        print(lines)  # 输出 ["第二行\n", "第三行\n"]  
    
  2. 大文件别乱用!
    readlines() 会把整个文件一次性读进内存。如果文件是几个 G 的日志,直接撑爆内存!这时应该用 readline() 逐行读,或分块处理。

  3. 换行符的处理
    返回的每行都带 \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 日

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值