Skip to main content
 首页 » 编程设计

python之有效地扩展 float 的 Pandas 字符串列

2025年05月04日38findumars

我有一个像这样的 DataFrame:

df = pd.DataFrame([['Col1Val', 'Col2Val', '[3, 31.1, -341.4, 54.13]']], 
                  columns=['col1', 'col2','values']) 

唯一的区别是我有几百万行和列 values 是每行正好有 200 个 float 的字符串,而不是我的示例中的 4 个。

包含此数据的 csv 文件约为 5 GB。但是,当我将前 2 个字符串列转换为类别后加载到 pandas 时,这会减少。因此,我能够在没有性能问题的情况下执行大多数操作(过滤、切片、索引)。

我需要将字符串的 values 列扩展为单独的 float 列。因此将有 200 列,每列包含一个 float 。我尝试执行此操作,但我总是内存不足。从理论上讲,我认为这应该可以以一种内存有效的方式逐行进行,因为 float 的列应该比字符串中的许多数字占用更少的内存。对此有什么好的算法?

我现有的代码如下,用于拆分 values 列。

df['values'] = df['values'].str.replace('[','').str.replace(']','') 
 
# code runs out of memory in next line! 
df_values = pd.DataFrame([x.split(',') for x in df['values'].values.tolist()]) 
 
df_values[df_values.columns] = df_values[df_values.columns].apply(pd.to_numeric, errors='coerce') 
df_values[df_values.columns] = df_values[df_values.columns].fillna(0.0) 
 
df= df.drop('values', 1).join(df_values) 

我的示例的预期结果,上面的代码为少量行正确生成:

df = pd.DataFrame([['Col1Val', 'Col2Val', 3.0, 31.1, -341.4, 54.13]], 
                  columns=['col1', 'col2', 0, 1, 2, 3]) 

为了解释为什么我希望(希望!)“减少内存”的解决方案,floats 通常应该比 string 占用更少的空间:

from sys import getsizeof 
 
getsizeof('334.34')      #55 
getsizeof(334.34)        #24 
getsizeof('-452.35614')  #59 
getsizeof(-452.35614)    #24 

请您参考如下方法:

选项 1
使用 ast.literal_eval/pd.eval 解析您的字符串列(这是最简单的第一步)。

import ast 
df['values'] = df['values'].apply(ast.literal_eval) 

接下来,展平最后一列并concat与剩余的 n - 1 列合并。

i = df.iloc[:, :-1] 
j = pd.DataFrame(df.iloc[:, -1].tolist()) 
 
pd.concat([i, j], 1) 
 
     col1     col2  0     1      2      3 
0  Col1Val  Col2Val  3  31.1 -341.4  54.13 

这是一个提高效率的改进版本。使用 del 就地删除列,并删除所有切片操作(它们创建副本,很浪费)。

j = pd.DataFrame(df['values'].tolist()) 
del df['values'] 
 
pd.concat([df, j], 1) 
 
      col1     col2  0     1      2      3 
0  Col1Val  Col2Val  3  31.1 -341.4  54.13 

选项 2
str.extractall(不能保证性能)。

df = df.set_index(['col1', 'col2'])['values']\ 
       .str.extractall('(\d+(?:\.\d*)?)')\ 
       .unstack() 
 
df.columns = df.columns.droplevel(0) 
df.reset_index() 
 
match     col1     col2  0     1      2      3 
0      Col1Val  Col2Val  3  31.1  341.4  54.13