首页 > 解决方案 > 有没有一种快速的方法可以将 Pandas 的列数据框转换为字符串列表?

问题描述

这在某种程度上与大多数人在列表和数据框之间转换时想做的事情相反。

我希望将大型数据帧(10M+ 行,20+ 列)转换为字符串列表,其中每个条目都是数据帧中每一行的字符串表示形式。我可以使用 pandas 的to_csv()方法来做到这一点,但我想知道是否有更快的方法,因为这被证明是我的代码中的一个瓶颈。

最小工作示例:

import numpy as np
import pandas as pd

# Create the initial dataframe.
size = 10000000
cols = list('abcdefghijklmnopqrstuvwxyz')
df = pd.DataFrame()
for col in cols:
    df[col] = np.arange(size)
    df[col] = "%s_" % col + df[col].astype(str)

# Convert to the required list structure
ret_val = _df_.to_csv(index=False, header=False).split("\n")[:-1]

对于我的 Core i9 的单个线程上的 10,000,000 行数据帧,上述代码的转换方面大约需要 90 秒,并且高度依赖于 CPU。如果可能的话,我希望将其降低一个数量级。

编辑:我不想将数据保存到 .csv 或文件中。我只是想将数据框转换为字符串数组。

编辑:只有 5 列的输入/输出示例:

In  [1]: df.head(10)
Out [1]:    a       b       c       d       e
         0  a_0     b_0     c_0     d_0     e_0
         1  a_1     b_1     c_1     d_1     e_1
         2  a_2     b_2     c_2     d_2     e_2
         3  a_3     b_3     c_3     d_3     e_3
         4  a_4     b_4     c_4     d_4     e_4
         5  a_5     b_5     c_5     d_5     e_5
         6  a_6     b_6     c_6     d_6     e_6
         7  a_7     b_7     c_7     d_7     e_7
         8  a_8     b_8     c_8     d_8     e_8
         9  a_9     b_9     c_9     d_9     e_9

In  [2]: ret_val[:10]
Out [2]: ['a_0,b_0,c_0,d_0,e_0',
          'a_1,b_1,c_1,d_1,e_1',
          'a_2,b_2,c_2,d_2,e_2',
          'a_3,b_3,c_3,d_3,e_3',
          'a_4,b_4,c_4,d_4,e_4',
          'a_5,b_5,c_5,d_5,e_5',
          'a_6,b_6,c_6,d_6,e_6',
          'a_7,b_7,c_7,d_7,e_7',
          'a_8,b_8,c_8,d_8,e_8',
          'a_9,b_9,c_9,d_9,e_9']

标签: pythonpython-3.xpandascsv

解决方案


我得到〜2.5倍的加速multiprocessing......

import multiprocessing

# df from OPs above code available in global scope

def fn(i):
    return df[i:i+1000].to_csv(index=False, header=False).split('\n')[:-1]

with multiprocessing.Pool() as pool:
    result = []
    for a in pool.map(fn, range(0, len(df), 1000)):
        result.extend(a)

在我的笔记本电脑上将 1M 行的总时间从 6.8 秒减少到 2.8 秒,因此有望在 i9 CPU 中扩展到更多内核。

这取决于 Unixfork语义来与子进程共享数据帧,并且显然做了更多的工作,但可能会有所帮助......

使用numpy.savetxtMassifox 的建议multiprocessing将其缩短到 2.0 秒,只需map以下功能:

def fn2(i):
    with StringIO() as fd:
        np.savetxt(fd, df[i:i+N], fmt='%s', delimiter=',')
        return fd.getvalue().split('\n')[:-1]

结果其他方面基本相同

您说“数据框是类中的变量”的评论可以通过多种不同的方式修复。一种简单的方法就是将数据帧传递给Pool initializer不会被选择的点(无论如何在Unix下)并将对它的引用存储在某个全局变量中。然后,每个工作进程都可以使用此引用,例如:

def stash_df(df):
    global the_df
    the_df = df

def fn(i):
    with StringIO() as fd:
        np.savetxt(fd, the_df[i:i+N], fmt='%s', delimiter=',')
        return fd.getvalue().split('\n')[:-1]

with multiprocessing.Pool(initializer=stash_df, initargs=(df,)) as pool:
    result = []
    for a in pool.map(fn, range(0, len(df), N)):
        result.extend(a)

只要每个Pool都由单个数据框使用,这就可以了


推荐阅读