听说 pandas 很好用,于是决定试一下
目的是这样的: user, amount, datetime A 10 2021-02-12 10:00:00 A 20 2021-03-12 10:00:00 B 30 2021-05-12 10:00:00 B 10 2021-06-12 10:00:00 C 5 2021-05-12 10:00:00 C 10 2021-06-12 10:00:00 C 20 2021-06-12 10:00:00 这是我们常见的数据库的表
最后要做成这样的效果 2021-02 2021-03 2021-05 2021-06 A 10 20 0 0 B 0 0 30 10 C 0 0 5 30
column_names = [desc[0] for desc in cursor.description] df = pd.DataFrame(rows, columns=column_names) df['datetime'] = pd.to_datetime(df['datetime']) df['归属年月'] =(df['datetime'].dt.year.astype(str)+ df['datetime'].dt.strftime('%m').astype(str)).astype(int)
到这里把时间字段理好了,变成数字,让它可以排序
grouped_data = df.groupby(['user', '归属年月'])['amount'].sum().reset_index() grouped_data = grouped_data.sort_values('归属年月') 来个二维分组,然后按照‘2021-05’这个来排序一下
df_pivot = pd.pivot_table(grouped_data, index=['user'], columns='归属年月', values='amount', aggfunc='sum').reset_index().fillna(0)
到这里就好了
代码十分简洁,但是这种代码真的有可读性吗?好维护吗?
如果我要算 A 用户所有金额汇总,可以这样写: df_pivot['余额'] = df_pivot.iloc[:, 1:].sum(axis=1)
这个= 让我震惊了,明明执行了函数操作,看上去像是一个赋值操作,这个 python 是怎么实现的?
我编辑器用的 vsc ,python 没有 type hint ,这个有解吗,难道一边写要一边查文档?
还有元组的简写法
a= 1,2,3
看到这个我也是懵了
有没有 pythoner 解答一下,这个语言真的适合写正经项目吗?
js 有 ts 就变得完美了,我很喜欢 python 的生态,有什么可以愉快写 python 的办法?
1
cmdOptionKana 268 天前
老生常谈了,越灵活的语言,越需要团队自己制定规范,否则很难维护。
以前 IDE 比较落后,Python 之类的动态语言很方便,但现在 IDE 先进了,优势就来到静态语言这边。 你可以想象一下(或者试一下),用记事本去写代码,你一定会更喜欢 Python 。可惜时代变了,Python 在现代确实没有特别优势。 |
2
woodytang OP 我昨天用了一下 python 的 fast-api, 这个设计得真好,,但是我对 python 没信心,用 python 做大项目的感觉是怎样的?
|
3
woodytang OP 另外很多 ai 的库都用 python 写的,听说很多搞 AI 的人都不怎么会编程,所以搞得效率很低下,是真的吗?
|
4
june4 268 天前
我虽然之前写了很多 python,但现在我更喜欢 js/ts 。js 保持了很好的语言简洁性/可读性/灵活性的平衡,ruby 简直可怕,python 搞得越来越复杂,但写起来体验不如 js
|
5
cmdOptionKana 268 天前
|
6
woodytang OP js 是在这些老 语言里面,一个沙盒脚本语言,感觉设计很合理的了,有 ts 后,感觉非常和谐,但是生态大多都是界面交互一类的,没啥意思,
python 功能强大多了,但是这个写起来蛋疼,可能是我不懂的缘故 |
7
FYFX 268 天前
好久没用过 pandas 了,不过感觉你这是写法问题。。。
而且我记得 pandas 的里面坑挺多的,不过听说好像[Polars]( https://docs.pola.rs/) 不错? |
8
Donahue 268 天前
python 有 type hint 的, 类型注解啊
比如 a:int = 10, def add(a:int, b:int) -> int: 跟 ts 差不多 |
9
mumbler 268 天前
python 正经项目还少吗
|
10
flyqie 268 天前
|
11
fatigue 268 天前
你懵是因为你用的少见得少,习惯了就好了,可读性是建立在大家都有基本共识的前提下的,你用上一年你就觉得太顺手了
|
13
sunzhuo 268 天前
python 不觉得容易,就是因为库多而已
|
14
zhzy 268 天前 1
1. 可能还是不熟悉吧, 首先那一大堆生成归属年月的代码其实直接格式化成字符串就行了, groupby 和 pivot_table 都是支持字符串的, 而且你也不需要先 groupby 再 pivot_table.
2. 至于 df_pivot['余额'] = df_pivot.iloc[:, 1:].sum(axis=1), 不就是把 sum 函数的返回值赋值给余额那一列么. 只是说它帮你处理了一下, 如果不存在这一列的话就新创建一列. 具体来说这是一个语法糖, 在类的 __setitem__ 方法里实现. 3. Python 有 type hint. 3.6 就有了, 不过要到好用的程度的话至少要到 3.9 和 3.10 吧. 4. 不要这样创建元组. 格式化工具会帮你加上括号的. 这个地方确实容易踩坑, 特别是只有一个元素的时候. 我是这样理解的, 在 Python 里 tuple 实际上是逗号定义而不是括号定义的. 5. 如果你是团队的话, 是会有规范的. 至于正经项目怎么说呢, 要写肯定能写, 毕竟 Instagram 也在用 (虽然是魔改的). 而且所有的语言都或多或少有一些黑魔法, 为了工程化不用就是了. 真要变成 Go 那样说实话有的时候也挺难受的. |
16
NoOneNoBody 268 天前 1
主要是你不懂 pandas/numpy ,所以觉得难读,用惯了的人,一眼就看得懂
1. df_pivot['余额'] = df_pivot.iloc[:, 1:].sum(axis=1) 这个确实是赋值,是整列批量赋值,用的是向量化函数,当数据量很大时,你就知道向量化计算的作用了 没有向量化函数,都不知道要写多少个 for 2. 可读性问题 在 pandas 的计算,一般不会太考究如何实现计算,更重视的是输入输出格式,以及值的类型和逻辑准确性,以及性能 因为 pandas 一般都是处理大量数据的,很难逐个值考究,只要保证格式、类型、逻辑准确性 例如有三百个白色乒乓球要变成红色,原生写法是逐个刷红色,pandas/numpy 是全部扔进红色颜料池,搅匀后捞上来就是了。所以需要搞清楚的是:扔进去的是否乒乓球、是否白色、多少个,颜料是否红色,池子是否容得下,以及捞起来后乒乓球有没有破损,数量够不够……至于中间如何染色搅匀,就只能相信这个操作搅匀的机器不会打烂乒乓球了 所以最好是函数加上 __doc__描述,便于以后查阅 np.lib.stride_tricks.as_strided(s, (len(s) - (window - 1), window), (s.values.strides * 2)) 这句我从别人那里抄过来的,至今都没搞清其中原理,但我知道 numpy 模拟实现 pandas.rolling 需要用到这句,且值和 pandas.roolling 的结果一致,这就够了 numpy 和 pandas 的手册很庞大,个人读不完,就算读完了也记不全,目前最好方案是借助 gpt 帮我查某个函数的意义 3.你这段代码并没有“规律时间序”,目的只是 groupby 分组,其实没必要用时间函数那么复杂,直接按字符串提取前 7 个字符,再 groupby 就可以了 vsc 有支持 hint 的扩展,如 pyright ,但如果代码没有写指定类型,也是按默认类型提示,所以想全程提示,需要自己在代码中指定 types hint |
17
vituralfuture 268 天前 via Android
元组一定要加括号,千万别省,可以配置格式化工具,每次格式化自动加上。
曾经在某行赋值语句末尾不小心按了逗号按键,刚好位置比较边缘,debug 半天,最后发现怎么值是个元组,才发现是这种无聊的原因 |
18
Jirajine 268 天前
R with tidyverse https://www.tidyverse.org/
这实质上是一个专为 dataframe 设计的基于 R 的 dsl ,可以说是单独这类需求理论上最适合的工具。 如果是一般语言的话,julia 也是非常适合数据分析的。 |
19
YsHaNg 268 天前 via iPhone
pandas/numpy 都算是 array oriented 类似的还有 Matlab 觉得懵你要不看看 apl
|
20
arischow 268 天前 via iPhone
你都这么想了,那肯定是不合适,用 Java 重写啦
|
21
dekuofa 268 天前 via iPhone
建议直接上 polars
|
22
bianhui 268 天前
静态语言后遗症。多写就好了。世界上流行的语言都有他自己的优势,你要抛开你固有观念,去接纳新鲜事物
|
23
nno 268 天前
"这个= 让我震惊了,明明执行了函数操作,看上去像是一个赋值操作,这个 python 是怎么实现的?"
这里其实是 pandas 的 DataFrame 重写了__setitem__接口,当你将对象当做一个 map 来操作的时候,就会触发__getitem__、__setitem__这些内置接口 |
24
darkengine 268 天前
光考虑要实现什么功能还不够,还需要考虑:
1 ,这个功能需要长期维护迭代,还是就一次性的数据处理 2 ,团队/个人的能力:包括能不能快速上手某个语言,熟悉某些库,使用某语音,某些库后续好不好招人 |
25
suuuch 268 天前
Python 在我的理解里面,是完全为了非科班的人员进入编程的最便捷的路径。
贴近自然语言的语法,脚本直接执行。在学习和自己想快速实现某些小功能的情况下,将环境准备成本将到最低。 比如说,openai 接口刚出来那会,我跟两个朋友弄了个账号一起用。我为了方便,在我买的美帝服务器上做一个转发,及我从国内把 请求服务器上的接口,再从服务器上转发到 openai 的 api 上。。写一下 flask 的固定 token 鉴权,用 supervisor 监控一下。不考虑任何性能。半个小时搞完。。。 |
26
SmiteChow 268 天前
还需要多用用,少提见解。
|
27
yjhatfdu2 268 天前
这不是一句 sql 查询就能解决的事情吗,为啥要变复杂?
|
28
xgdgsc 268 天前
不喜欢这种可以用 julia 无脑 for 循环处理数据
|
29
song135711 268 天前
ide 试试 pycharm 社区版。
计算机语言是工具,重要的是使用工具的人。 |
33
yjhatfdu2 264 天前
@woodytang pg 下,如果可以用程序生成返回列然后拼接 SQL ,那么很简单
//创建测试表 create table test3("user" text,amount int,datetime timestamp); insert into public.test3 (user, amount, datetime) values ('A', 10, '2021-02-12 10:00:00.000000'), ('A', 20, '2021-03-12 10:00:00.000000'), ('B', 30, '2021-05-12 10:00:00.000000'), ('B', 10, '2021-06-12 10:00:00.000000'), ('C', 10, '2021-06-12 10:00:00.000000'), ('C', 20, '2021-06-12 10:00:00.000000'), ('C', 5, '2021-05-12 10:00:00.000000'); //使用 crosstab 进行 pivot select * from crosstab($$select "user",date_trunc('month',datetime)::date::text,sum(amount) from test3 group by 1,2 order by 1,2 $$, 'select distinct date_trunc(''month'',datetime)::date::text from test3 order by 1') as ct( "user" text, "2021-02" text, "2021-03" text, "2021-05" text, "2021-06" text ); //结果 user | 2021-02 | 2021-03 | 2021-05 | 2021-06 ------+---------+---------+---------+--------- A | 10 | 20 | | B | | | 30 | 10 C | | | 5 | 30 如果要动态生成返回列名,那么确实是相当的麻烦,因为 pg 的查询必须显式制定列,只能用非常别扭的方式 -- 创建一个函数,实际查询的 SQL ,可以反复使用 create or replace function crosstabquery(_t anyelement) returns setof anyelement as $$ declare column_list text; begin select string_agg(format('%I text', d), ',') into column_list from (select distinct date_trunc('month', datetime)::date::text d from test3 order by 1) r; return query execute ($b$select * from crosstab($a$select "user",date_trunc('month',datetime)::date::text,sum(amount) from test3 group by 1,2 order by 1,2 $a$, 'select distinct date_trunc(''month'',datetime)::date::text from test3 order by 1') as ct("user" text,$b$ || column_list||')' ); end ; $$ language plpgsql; -- 创建返回类型,每次使用前调用 do $$ declare column_list text; begin select string_agg(format('%I text', d), ',') into column_list from (select distinct date_trunc('month', datetime)::date::text d from test3 order by 1) r; drop type if exists __t; execute format('create type __t as ("user" text, %s)', column_list); end $$ language plpgsql; -- 返回实际结果 select * from crosstabquery(null::__t); user | 2021-02-01 | 2021-03-01 | 2021-05-01 | 2021-06-01 ------+------------+------------+------------+------------ A | 10 | 20 | | B | | | 30 | 10 C | | | 5 | 30 (3 rows) 不过如果可以动态生成 sql ,就别用下面这个方案,实在是别扭 |