python数据分析一例:使用SQL和pandas对数据进行聚合和diff

server/2024/12/16 15:07:52/

对一系列数据聚合后进行diff,是一种常见的数据分析需求。例如,我们可能会需要将每个月的财务支出流水数据进行分类汇总,再对不同月的汇总数据进行比较,看看哪些分类支出变多了,哪些变少了。此次我将使用SQL和pandas来实现上面所述需求,具体来说,使用SQL实现数据聚合功能,使用pandas对聚合后的数据进行diff。

虽说单独使用pandas也足够实现这个需求,但是考虑到类似的流水数据很多都是存在DB里的,检索数据时聚合一下也就顺手的事,因此聚合数据就交给SQL来做了。况且SQL相比pandas性能更好,泛用性也更广,如果不是用SQL来diff数据没有比较直观的方案,完全使用SQL来实现这个需求更加符合我的心意。本文中使用的数据库是python内置的sqlite3。

Talk is cheap,直接上代码,解释和注意事项都在注释里面了:

python">#!/usr/bin/python3from collections import namedtuple
import logging
import os
import sqlite3import pandas as pdlogging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')DB_NAME = "expenses_aggregate_diff.db"# 流水记录,总共四个字段:部门、类型、月份和金额,一般来说月份这个字段应该细化为时间戳,这里为了简化就用月份了
Record = namedtuple('Record', ['department', 'type', 'month', 'amount'])# 插一些样例数据
def insert_example_records():records = [Record('A', 'R&D', '202406', 1000),Record('A', 'Operating', '202406', 2000),Record('A', 'R&D', '202406', 4000),Record('A', 'R&D', '202406', 5000),Record('B', 'Operating', '202406', 2000),Record('B', 'Sales', '202406', 8000),Record('A', 'Operating', '202407', 7000),Record('B', 'Operating', '202407', 4000),Record('B', 'Sales', '202407', 2000),Record('B', 'R&D', '202407', 1000),Record('B', 'R&D', '202407', 9000),]logging.info(f'save {len(records)} records to db')conn = sqlite3.connect(DB_NAME)cursor = conn.cursor()cursor.execute('''CREATE TABLE IF NOT EXISTS records (id INTEGER PRIMARY KEY AUTOINCREMENT,department TEXT NOT NULL,type TEXT NOT NULL,month TEXT NOT NULL,amount INT NOT NULL)''')for record in records:cursor.execute('''INSERT INTO records (department, type, month, amount)VALUES (?, ?, ?, ?)''', (record.department, record.type, record.month, record.amount))conn.commit()conn.close()# 获得某个月份聚合后的流水数据,以group_by_fields为分组字段,可接受的参数是部门、类型或两者的组合,对金额进行聚合
def get_aggregated_amount_by_month(month: str, group_by_fields: list[str]) -> pd.DataFrame:conn = sqlite3.connect(DB_NAME)cursor = conn.cursor()# 为了简便,就不对group_by_fields做校验了,这种代码绝不能上产线的 XDgroup_by_clause = ", ".join(group_by_fields)sql = f"""SELECT {group_by_clause}, SUM(amount) FROM records WHERE month = ? GROUP BY {group_by_clause}"""cursor.execute(sql, (month,))rows = cursor.fetchall()conn.close()logging.info(f'get {len(rows)} records for {month}')return pd.DataFrame(rows, columns=group_by_fields + ['amount'])# diff某两个月聚合后的数据
def diff_aggregated_data(month0: str, month1: str, group_by_fields: list[str]) -> pd.DataFrame:# 聚合df0 = get_aggregated_amount_by_month(month0, group_by_fields)df1 = get_aggregated_amount_by_month(month1, group_by_fields)# 合并到一个表里面,合并方式为outer join,缺失项会被填充为NaNdf_diff = pd.merge(df0, df1, on=group_by_fields, how='outer', suffixes=(month0, month1))# 将NaN填充为0df_diff = df_diff.fillna(0)# 对数据进行diffdf_diff['amount_diff'] = df_diff['amount' + month1] - df_diff['amount' + month0]# 只保留diff后的数据df_diff = df_diff[group_by_fields + ['amount_diff']]return df_diffif __name__ == '__main__':if not os.path.exists(DB_NAME):insert_example_records()group_by_fields = ['department', 'type']month0 = '202406'month1 = '202407'print(diff_aggregated_data(month0, month1, group_by_fields))

输出为:

  department       type  amount_diff
0          A  Operating       5000.0
1          A        R&D     -10000.0
2          B  Operating       2000.0
3          B        R&D      10000.0
4          B      Sales      -6000.0

http://www.ppmy.cn/server/150660.html

相关文章

CTF 攻防世界 Web: FlatScience write-up

题目名称-FlatScience 网址 index 目录中没有发现提示信息,链接会跳转到论文。 目前没有发现有用信息,尝试目录扫描。 目录扫描 注意到存在 robots.txt 和 login.php。 访问 robots.txt 这里表明还存在 admin.php admin.php 分析 在这里尝试一些 sql…

Python什么是动态调用方法?What is Dynamic Method Invocation? (中英双语)

什么是动态调用方法? 动态调用方法指通过方法或属性的名称,在运行时而非编译时调用对象的方法或访问其属性。换句话说,在编写代码时,方法名或属性名可以是变量,只有在程序运行时才能确定调用的内容。这种特性允许程序…

机器学习干货笔记分享:朴素贝叶斯算法

朴素贝叶斯分类是一种十分简单的分类算法,即对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。 以判定外国友人为例做一个形象的比喻。 若我们走在街上看到一个黑皮肤的外…

Elasticsearch的一些介绍

你想问的可能是 **Elasticsearch**,以下是关于它的一些介绍: ### 概述 Elasticsearch是一个基于Apache Lucene库构建的开源分布式搜索和分析引擎,采用Java语言编写,具有高性能、可扩展性和易用性等特点,可用于各种数据…

Unity学习笔记(一)如何实现物体之间碰撞

前言 本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记 如何实现物体之间碰撞 实现物体之间的碰撞关键组件:Rigidbody 2D(刚体)、Collider 2D(碰撞体)、Sprite Renderer(Sprite渲染器) 实现物体之间的碰撞 …

基于yolov10的遥感影像目标检测系统,支持图像检测,视频检测和实时摄像检测功能(pytorch框架,python源码)

更多目标检测、图像分类识别、目标检测等其他项目可看我主页其他文章 功能演示: 基于yolov10的遥感影像目标检测系统,既支持图像检测,也支持视频和摄像实时检测【pytorch框架、python源码】_哔哩哔哩_bilibili (一)…

springboot432校园疫情防控系统(论文+源码)_kaic

摘 要 国家及社会对高等教育的支持度在近年来呈直线上升,人民也了解到教育的重要性,因此我国的高校数量及在校生数量也逐年递增,人数的增加本就加大了高校的管理工作难度,加之2019年底新型冠状肺炎疫情的爆发,使高校的…

【java常用集合】java

第1关:初识Collection 第2关:集合遍历方法 第3关:Set接口 第4关:Map接口 第5关:泛型 第6关:知识回顾