Pandas DataFrame中存储为字符串的列表:如何转换回列表

151 投票
9 回答
141377 浏览
提问于 2025-04-18 02:59

我有一个 nm 列的 Pandas 数据框 df,定义如下。(我知道这不是最好的做法,但对我实际代码来说是有意义的,不过这在这里说太多了,所以就相信我,这种方法在我的特定场景下是有效的。)

>>> df = DataFrame(columns=['col1'])
>>> df.append(Series([None]), ignore_index=True)
>>> df
Empty DataFrame
Columns: [col1]
Index: []

我在这个数据框的单元格里存储了列表,方法如下。

>>> df['column1'][0] = [1.23, 2.34]
>>> df
     col1
0  [1, 2]

但不知道为什么,这个数据框把这个列表存成了字符串,而不是列表。

>>> df['column1'][0]
'[1.23, 2.34]'

我有两个问题想问你。

  1. 为什么数据框会把列表存成字符串?有没有办法解决这个问题?
  2. 如果没有,那有没有一种 Python 的方式可以把这个字符串转换成列表?

更新

我使用的数据框是从 CSV 格式保存和加载的。是这个格式,而不是数据框本身,把列表从字符串转换成了字面量。

9 个回答

3

1) 有办法解决这个问题。这里可以用 loc 来帮助你。

>>> import pandas as pd

>>> df = pd.DataFrame(columns=['column1'])
>>> df = df.append(pd.Series(data = {'column1':[None]}), ignore_index = True)

   column1
0  [None]

>>> # Add list to index 0 in column1
>>> df.loc[0,'column1'] = [1.23, 2.34]
>>> print(df.loc[0, 'column1'])
[1.23, 2.34]

2) 用 Python 的方式把这个字符串转换成一个列表。(这可能正是你想要的,因为你使用的数据框是从 CSV 格式保存和加载的,这里有几种解决方案)。这是对 pshep123 回答的补充。

from ast import literal_eval
import pandas as pd

csv = io.StringIO(u'''
id  list
A1  [1,2]
A2  [3,4]
A3  [5,6]
''')
df = pd.read_csv(csv, delim_whitespace = True)

# Output is a string
df.loc[0, 'list']
'[1,2]'

# Convert entire column to a list
df.loc[:,'list'] = df.loc[:,'list'].apply(lambda x: literal_eval(x))

# Output is a list
df.loc[0, 'list']
[1, 2]
13

我刚遇到这个问题,发现有一个非常简单的解决办法(pandas.eval())。我使用的是pandas 0.20.0版本。

# SETUP
import pandas as pd
import io

csv = io.StringIO(u'''
id  list
A1  [1,2]
A2  [3,4]
A3  [5,6]
''')

df = pd.read_csv(csv, delim_whitespace = True)

# TYPE CHECK <type 'str'>
print type(df.at[0, 'list'])

# MAIN CONVERSION
df['list'] = pd.eval(df['list'])

# TYPE CHECK <type 'list'>
print type(df.at[0, 'list'])
37
  • 使用 ast.literal_eval 来安全地评估一个包含 Python 字面量或 容器数据类型 的字符串。
    • 这是 Python 标准库的一部分。

    • 使用 python 的 eval() 和 ast.literal_eval() 的区别 解释了为什么 literal_eval 比使用 eval 更安全。

    • 示例:

      • literal_eval("[1.23, 2.34]") 可以正常工作。
      • literal_eval("['KB4523205','KB4519569','KB4503308']") 也可以正常工作。
        • 其他答案提到 pd.eval,但它的使用有限;在这个简单的例子中会出现 ValueError: NumExpr 2 does not support Unicode as a dtype. 的错误。
      • literal_eval("[KB4523205, KB4519569, KB4503308]") 不可以工作(因为 str 值没有加引号)。
  • 在读取文件时,可以通过使用 pandas.read_csvconverters 参数来转换列。

test.csv 中的数据

col1
"[1.23, 2.34]"
"['KB4523205','KB4519569','KB4503308']"

在创建 csv 时转换列

from ast import literal_eval
import pandas as pd

# convert the column during import
df = pd.read_csv('test.csv', converters={'col1': literal_eval})

# display(df)
                                col1
0                       [1.23, 2.34]
1  [KB4523205, KB4519569, KB4503308]

# check type
print(type(df.iloc[0, 0]))
list

print(type(df.iloc[1, 0]))
list

转换现有数据框的列

df.col1 = df.col1.apply(literal_eval)

%%timeit

  • pd.eval 的速度比 literal_eval 慢 28 倍。
  • 假设 test.csv 有 2,820,511 行数据,每行是 "[1.23, 2.34]"

enter image description here

100

你可以直接使用 pandas -

import pandas as pd
df = pd.read_csv(DF_NAME, converters={'COLUMN_NAME': pd.eval})

这样做会把那一列的数据按照它在 Python 中对应的数据类型读取,而不是当成字符串。

更新:

正如 @ctwardy 在评论中指出的,使用 pd.eval 比使用 eval 更明智,这样可以避免一些意外的正则表达式相关的问题。详细信息可以查看这个链接 - https://realpython.com/python-eval-function/#minimizing-the-security-issues-of-eval

152

正如你所提到的,这种情况在保存和加载 pandas 的数据框(DataFrame)为 .csv 文件时很常见,因为 .csv 是一种文本格式。

在你的例子中,这种情况发生是因为列表对象有字符串表示,这样它们就可以被存储为 .csv 文件。加载这个 .csv 文件时,就会得到那个字符串表示。

如果你想保存实际的对象,应该使用 DataFrame.to_pickle()(注意:对象必须是可序列化的!)。

至于你的第二个问题,你可以使用 ast.literal_eval 将其转换回来:

>>> from ast import literal_eval
>>> literal_eval('[1.23, 2.34]')
[1.23, 2.34]

撰写回答