V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
yaleyu
V2EX  ›  Python

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

  •  
  •   yaleyu · 2021-02-10 11:25:55 +08:00 · 4101 次点击
    这是一个创建于 1413 天前的主题,其中的信息可能已经有所发展或是发生改变。

    原始数据结构如下,总共 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
    
    19 条回复    2021-02-12 00:05:53 +08:00
    bigtan
        1
    bigtan  
       2021-02-10 11:33:15 +08:00 via iPhone
    pd.Dataframe.from_dict
    bigtan
        2
    bigtan  
       2021-02-10 11:35:54 +08:00 via iPhone
    你不是生成了一张表,而是几万张
    binux
        3
    binux  
       2021-02-10 11:48:32 +08:00 via Android
    一次把数据喂给 dataframe
    lv2016
        4
    lv2016  
       2021-02-10 11:57:15 +08:00
    直接修改 dataframe 确实很慢,建议先生成 list,再转 dataframe
    WinG
        5
    WinG  
       2021-02-10 12:03:41 +08:00
    量化炒股???
    yaleyu
        6
    yaleyu  
    OP
       2021-02-10 12:54:47 +08:00
    @lv2016 试过先扁平化 dict 成 list,再赋值给 df,时间稍晚缩短了一丢丢,不过不是很明显。
    yaleyu
        7
    yaleyu  
    OP
       2021-02-10 12:55:21 +08:00
    @bigtan 谢谢,我研究一下这个,新年快乐哈。
    princelai
        8
    princelai  
       2021-02-10 12:59:51 +08:00   ❤️ 1
    楼上说了,每次操作 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
        9
    princelai  
       2021-02-10 13:01:00 +08:00
    缩进乱了,new_funds.append 是在两层循环里,json_normalize 是在最外层,循环完毕才去执行的
    Escapist367
        10
    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
        11
    yaleyu  
    OP
       2021-02-10 13:43:52 +08:00
    @princelai 缩进乱了没事,好像 V2 回复不能用 markdown 格式。

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

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

    pandas 的优势在 dataframe 的批处理上,尽量把计算操作放到 dataframe/series 的原生方法里进行,大部分需求应该都能满足。
    lixuda
        15
    lixuda  
       2021-02-10 19:47:11 +08:00
    pandas 不要用 for,用 apply 。
    allAboutDbmss
        16
    allAboutDbmss  
       2021-02-11 02:24:15 +08:00
    https://github.com/modin-project/modin
    我每天都向人推荐这个
    owenliang
        17
    owenliang  
       2021-02-11 09:10:36 +08:00
    学学 pyspark 。
    fxrocks
        18
    fxrocks  
       2021-02-11 10:46:42 +08:00 via Android
    1 楼正解
    volvo007
        19
    volvo007  
       2021-02-12 00:05:53 +08:00 via iPhone
    还有个函数
    pd.json_normalize,可以把半结构化的 json (比如数组套对象又套数组而且不同组嵌套不一样的)转为字典,而且可以有选择性地抽取 json 中的某些字段
    贼好用
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1140 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 18:35 · PVG 02:35 · LAX 10:35 · JFK 13:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.