熊猫 DataFrame 存储列表为字符串: 如何转换回列表

我有一个 N-by-熊猫数据帧 df定义如下。(我知道这不是最好的方法。这对于我在实际代码中尝试做的事情来说是有意义的,但是对于这篇文章来说就是 TMI 了,所以请相信我的话,这种方法在我的特定场景中是有效的。)

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

我将列表存储在这个 DataFrame 的单元格中,如下所示。

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

由于某种原因,DataFrame 将此列表存储为字符串而不是列表。

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

我有两个问题要问你。

  1. DataFrame 为什么将列表存储为字符串,是否有办法解决这个问题?
  2. 如果没有,那么是否有一种 Python 方法可以将这个字符串转换成一个列表?

更新

我使用的 DataFrame 已经从 CSV 格式保存和加载

112830 次浏览

正如您指出的,这种情况通常发生在将熊猫 DataFrames 保存和加载为 .csv文件时,.csv文件是一种文本格式。

在您的例子中发生这种情况是因为列表对象具有字符串表示形式,允许将它们存储为 .csv文件。然后加载 .csv将生成该字符串表示形式。

如果要存储实际的对象,应该使用 DataFrame.to_pickle()(注意: 对象必须是 picklable!)。

要回答第二个问题,你可以用 ast.literal_eval把它转换回来:

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

仅供参考... 熊猫不会把列表转换成字符串..。

In [29]: data2 = [{'a': [1, 5], 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]


In [30]: df = pd.DataFrame(data2)


In [31]: df
Out[31]:
a   b   c
0  [1, 5]   2 NaN
1       5  10  20


In [32]: df['a'][0], type(df['a'][0])
Out[32]: ([1, 5], list)


In [33]: pd.__version__
Out[33]: '0.12.0'

我也有同样的问题。当使用 df.to _ CSV ()将数据框列表列存储到 CSV 文件时,列表列被转换为字符串,例如“[42,42,42]”而不是[42,42,42]

Alex 的回答是正确的,您可以使用 literal_eval将字符串转换回一个列表。这种方法的问题在于,您需要导入一个额外的库,并且需要将该函数应用或映射到您的数据框架。同样简单的方法是强制 Panda 将该列作为 Python 对象(dtype)读取

df["col1"].astype('O')

O 用于包括列表的 Python 对象。更多信息 给你。请注意,如果解析空列表字符串,此方法将失败: “[]”

或者,您也可以将一个函数应用到您的列(这个函数用于整数) :

def stringToList(string):
# input format : "[42, 42, 42]" , note the spaces after the commas, in this case I have a list of integers
string = string[1:len(string)-1]
try:
if len(string) != 0:
tempList = string.split(", ")
newList = list(map(lambda x: int(x), tempList))
else:
newList = []
except:
newList = [-9999]
return(newList)


df["col1"] = df["col1"].apply(lambda x: stringToList(x))

我只是碰到了这个问题,有一个非常简单的解决方案(Pandas.eval ())。我使用熊猫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'])

1)有一种方法可以避免这种行为。在这里使用 loc help。

>>> 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 方法。(这可能是您想要的,因为您正在使用的 DataFrame 已经从 CSV 格式保存和加载,有一些解决方案可以解决这个问题)。这是在第123页的答案上加上的。

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]

你可以直接使用熊猫-

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

这将把该列读取为 python 中对应的 dtype,而不是字符串。

更新:

正如@ctwardy 在评论中正确指出的那样。更明智的做法是使用 pd.eval而不是 eval,以避免意外的与正则表达式相关的后果。详情 -https://realpython.com/python-eval-function/#minimizing-the-security-issues-of-eval

我使用的一个简单方法是调用一个 lambda 函数,该函数对第一个和最后一个元素进行索引(list 方括号为 str 格式) ,然后调用 split 方法,接着调用另一个方法,用 int 替换 list 元素。

df['column1'] = df['column1'].apply(lambda x:x[1:-1].split(',')).apply(lambda x:[int(i) for i in x])

添加到 亚历克斯的答案。这里是另一个版本,可以用来转换个别项目从字符串到列表

import pandas as pd
from ast import literal_eval


df = pd.read_csv("some_csvfile.csv")


def item_gen(l):
for i in l:
yield(i)


for i in item_gen(df["some_column_with_list_item"]):
print(literal_eval(i))

数据

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.evalliteral_eval慢28倍
  • 给定具有2,820,511行 "[1.23, 2.34]"test.csv

enter image description here