pandas 花了 7 分 32 秒生成一张[45639 rows x 7 columns]的表,实在是太太太慢了,求更快的方法

2021-02-10 11:25:55 +08:00
 yaleyu

原始数据结构如下,总共 7000 多条,想扁平化然后放到 pd 里面做进一步的处理

>>> len(funds)
7597
>>> funds[0]
{'000005': {'company': '嘉实', 'name': '嘉实增强信用定期债券', 'type': '定开债券', 'earning': {'1m': 0.1, '3m': 1.0, '6m': 0.85, '1y': 2.59}, 'hold_stocks': [{'code': '601966', 'name': '玲珑轮胎', 'percentage': '0.51%', 'volume': 0.67, 'value': 23.48}, {'code': '002745', 'name': '木林森', 'percentage': '0.47%', 'volume': 1.5, 'value': 21.92}]}}
>>> funds[-1]
{'970008': {'company': '华安', 'name': '华安证券汇赢增利一年持有混合 C', 'type': '混合型', 'earning': {'1m': -0.55, '3m': -0.58, '6m': 0.67, '1y': 0}, 'hold_stocks': [{'code': '300692', 'name': '中环环保', 'percentage': '0.72%', 'volume': 31.81, 'value': 496.79}, {'code': '603012', 'name': '创力集团', 'percentage': '0.67%', 'volume': 72.28, 'value': 465.48}, {'code': '300197', 'name': '铁汉生态', 'percentage': '0.64%', 'volume': 138.93, 'value': 440.39}, {'code': '002562', 'name': '兄弟科技', 'percentage': '0.23%', 'volume': 31.0, 'value': 160.27}]}}
>>>

生成 df 代码如下:

df = pd.DataFrame(columns=['fund_code', 'fund_company', 'stock_code', 'stock_name', 'stock_percentage', 'stock_volume', 'stock_value'])
i = 0
for fund in funds:
    fund_code = list(fund.keys())[0]
    fund_company = list(fund.values())[0]['company']
    if list(fund.values())[0]['hold_stocks']:
        for hold_stock in list(fund.values())[0]['hold_stocks']:
            stock_code = hold_stock['code'].strip()
            stock_name = hold_stock['name'].strip()
            stock_percentage = float(hold_stock['percentage'].strip('%'))
            stock_volume = hold_stock['volume']
            stock_value = hold_stock['value']
            df.loc[i] = [fund_code, fund_company, stock_code, stock_name, stock_percentage, stock_volume, stock_value]
            i += 1
    else:
        df.loc[i] = [fund_code, fund_company, '', '', 0, 0, 0]  # 不持仓任何股票则仅记录基金代码和基金公司
        i += 1
4060 次点击
所在节点    Python
19 条回复
bigtan
2021-02-10 11:33:15 +08:00
pd.Dataframe.from_dict
bigtan
2021-02-10 11:35:54 +08:00
你不是生成了一张表,而是几万张
binux
2021-02-10 11:48:32 +08:00
一次把数据喂给 dataframe
lv2016
2021-02-10 11:57:15 +08:00
直接修改 dataframe 确实很慢,建议先生成 list,再转 dataframe
WinG
2021-02-10 12:03:41 +08:00
量化炒股???
yaleyu
2021-02-10 12:54:47 +08:00
@lv2016 试过先扁平化 dict 成 list,再赋值给 df,时间稍晚缩短了一丢丢,不过不是很明显。
yaleyu
2021-02-10 12:55:21 +08:00
@bigtan 谢谢,我研究一下这个,新年快乐哈。
princelai
2021-02-10 12:59:51 +08:00
楼上说了,每次操作 df 是很慢的,可以操作 json 变为适合的格式,然后一次性给 pandas

```
from pandas import json_normalize

funds = [{'000005': {'company': '嘉实', 'name': '嘉实增强信用定期债券', 'type': '定开债券', 'earning': {'1m': 0.1, '3m': 1.0, '6m': 0.85, '1y': 2.59},
'hold_stocks': [{'code': '601966', 'name': '玲珑轮胎', 'percentage': '0.51%', 'volume': 0.67, 'value': 23.48},
{'code': '002745', 'name': '木林森', 'percentage': '0.47%', 'volume': 1.5, 'value': 21.92}]}},
{'970008': {'company': '华安', 'name': '华安证券汇赢增利一年持有混合 C', 'type': '混合型', 'earning': {'1m': -0.55, '3m': -0.58, '6m': 0.67, '1y': 0},
'hold_stocks': [{'code': '300692', 'name': '中环环保', 'percentage': '0.72%', 'volume': 31.81, 'value': 496.79},
{'code': '603012', 'name': '创力集团', 'percentage': '0.67%', 'volume': 72.28, 'value': 465.48},
{'code': '300197', 'name': '铁汉生态', 'percentage': '0.64%', 'volume': 138.93, 'value': 440.39},
{'code': '002562', 'name': '兄弟科技', 'percentage': '0.23%', 'volume': 31.0, 'value': 160.27}]}}]

new_funds = []
for fund in funds:
for k, v in fund.items():
v.update({'code': k})
new_funds.append(v)
df = json_normalize(new_funds, 'hold_stocks', ['company', 'name', 'type', 'code'], meta_prefix='fund_', record_prefix='stock_')

```
princelai
2021-02-10 13:01:00 +08:00
缩进乱了,new_funds.append 是在两层循环里,json_normalize 是在最外层,循环完毕才去执行的
Escapist367
2021-02-10 13:41:45 +08:00
df_list=[]
i = 0
for fund in funds:
fund_code = list(fund.keys())[0]
fund_company = list(fund.values())[0]['company']
if list(fund.values())[0]['hold_stocks']:
for hold_stock in list(fund.values())[0]['hold_stocks']:
stock_code = hold_stock['code'].strip()
stock_name = hold_stock['name'].strip()
stock_percentage = float(hold_stock['percentage'].strip('%'))
stock_volume = hold_stock['volume']
stock_value = hold_stock['value']
df_list.append([fund_code, fund_company, stock_code, stock_name, stock_percentage, stock_volume, stock_value])
i += 1
else:
df_list.append([fund_code, fund_company, '', '', 0, 0, 0]) # 不持仓任何股票则仅记录基金代码和基金公司
i += 1

df = pd.DataFrame(df_list,columns=['fund_code', 'fund_company', 'stock_code', 'stock_name', 'stock_percentage', 'stock_volume', 'stock_value'])


==================
在你基础上最小改动。
你先用一个 list 存放每行结果,再一次性转 df 就行了,大概只要 1s
yaleyu
2021-02-10 13:43:52 +08:00
@princelai 缩进乱了没事,好像 V2 回复不能用 markdown 格式。

你这个太牛了,时间从 7 分多钟缩短到 0.5 秒了,不过数据从 45639 行减少成了 42138 行,我再仔细检查一下哪些数据被抛弃了。

新年快乐哈。
billgreen1
2021-02-10 13:51:39 +08:00
@princelai 这个赞,我之前都不了解这个函数
youthfire
2021-02-10 13:57:47 +08:00
pandas 速度还是很快的,我的经验是基本上百万级的,论分钟的话多半是嵌套写错了在低效执行。数据库处理的话优势更明显。
winglight2016
2021-02-10 15:10:52 +08:00
你用了两层 for 然后调用 loc 方法,当然会慢呀。记得减少 for 循环次数以及 loc 调用次数。

pandas 的优势在 dataframe 的批处理上,尽量把计算操作放到 dataframe/series 的原生方法里进行,大部分需求应该都能满足。
lixuda
2021-02-10 19:47:11 +08:00
pandas 不要用 for,用 apply 。
allAboutDbmss
2021-02-11 02:24:15 +08:00
https://github.com/modin-project/modin
我每天都向人推荐这个
owenliang
2021-02-11 09:10:36 +08:00
学学 pyspark 。
fxrocks
2021-02-11 10:46:42 +08:00
1 楼正解
volvo007
2021-02-12 00:05:53 +08:00
还有个函数
pd.json_normalize,可以把半结构化的 json (比如数组套对象又套数组而且不同组嵌套不一样的)转为字典,而且可以有选择性地抽取 json 中的某些字段
贼好用

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/752784

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX