【问题标题】:Pandas DataFrame stored list as string: How to convert back to listPandas DataFrame 将列表存储为字符串:如何转换回列表
【发布时间】:2014-05-31 11:25:57
【问题描述】:

我有一个 n-by-m Pandas DataFrame 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]'

我有 2 个问题要问你。

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

更新

我使用的 DataFrame 已从 CSV 格式保存和加载。 这种格式,而不是 DataFrame 本身,将列表从字符串转换为文字。

【问题讨论】:

标签: python string list pandas dataframe


【解决方案1】:
  • 使用ast.literal_eval 安全地评估包含Python 文字或container datatype 的字符串。
    • 它是标准库的一部分

    • Using python's eval() vs. 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.evalliteral_eval 慢 28 倍
  • 给定 test.csv 和 2,820,511 行 "[1.23, 2.34]"

【讨论】:

    【解决方案2】:

    你可以直接使用 pandas -

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

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

    更新:

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

    【讨论】:

    • 请确保您使用的是 pandas.eval 而不是内置 eval。如果您的 csv 文件中有正确的表达式,内置 eval 将愉快地删除您的所有文件。见realpython.com/python-eval-function/…
    • @markroxor - 更新:ast.literal_eval 快了 15 倍,而且看起来更多,pd.eval 似乎可以退回到 Python eval。
    • 这很好 - pd.eval 有一个 python 引擎,但这仍然是有限的语法。它拒绝解析__import__ 甚至sum。 (literal_eval 对于这个用例来说似乎更快。)
    【解决方案3】:

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

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

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

    要回答您的第二个问题,您可以使用ast.literal_eval 将其转换回来:

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

    【讨论】:

      【解决方案4】:

      添加到Alex's 答案。这是另一个版本,可用于将单个项目从字符串转换为列表

      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))
      

      【讨论】:

        【解决方案5】:

        我使用的一个简单技巧是调用一个 lambda 函数,该函数索引第一个和最后一个元素(str 形式的列表括号)并调用 split 方法,然后调用另一个用整数替换列表元素的方法。

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

        【讨论】:

          【解决方案6】:

          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) 将此字符串转换为列表的 Pythonic 方式。 (这可能是您想要的,因为您正在使用的 DataFrame 已经从 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]
          

          【讨论】:

            【解决方案7】:

            我刚遇到这个问题,有一个非常简单的解决方案 (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'])
            

            【讨论】:

              【解决方案8】:

              我遇到了同样的问题。使用 df.to_csv() 将数据框列表列存储到 CSV 文件时,列表列将转换为字符串,例如"[42, 42, 42]" 代替 [42, 42, 42]

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

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

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

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

              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))
              

              【讨论】:

                【解决方案9】:

                仅供参考... pandas 不会将列表转换为字符串。 ..

                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'
                

                【讨论】:

                  猜你喜欢
                  • 2020-11-20
                  • 2018-07-13
                  • 2021-11-10
                  • 2013-02-13
                  • 2020-09-03
                  • 2014-01-05
                  • 2021-11-28
                  • 1970-01-01
                  相关资源
                  最近更新 更多