Gespeicherte Pandas DataFrame-Liste als Zeichenfolge: Wie konvertiere ich zurück in die Liste?

74

Ich habe einen n- mal- m Pandas DataFrame dfwie folgt definiert. (Ich weiß, dass dies nicht der beste Weg ist, dies zu tun. Es ist sinnvoll für das, was ich in meinem eigentlichen Code versuche, aber das wäre TMI für diesen Beitrag. Nehmen Sie also einfach mein Wort, dass dieser Ansatz in meinem speziellen Szenario funktioniert .)

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

Ich habe Listen in den Zellen dieses DataFrame wie folgt gespeichert.

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

Aus irgendeinem Grund hat der DataFrame diese Liste als Zeichenfolge anstelle einer Liste gespeichert.

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

Ich habe 2 Fragen an Sie.

  1. Warum speichert der DataFrame eine Liste als Zeichenfolge und gibt es einen Weg, um dieses Verhalten zu umgehen?
  2. Wenn nicht, gibt es dann eine pythonische Möglichkeit, diese Zeichenfolge in eine Liste umzuwandeln?

Aktualisieren

Der von mir verwendete DataFrame wurde gespeichert und aus einem CSV-Format geladen. Dieses Format konvertierte die Liste anstelle des DataFrame selbst von einer Zeichenfolge in ein Literal.

Gyan Veda
quelle

Antworten:

88

Wie Sie bereits betont haben, kann dies häufig beim Speichern und Laden von Pandas DataFrames als .csvDateien auftreten, bei denen es sich um ein Textformat handelt.

In Ihrem Fall geschah dies, weil Listenobjekte eine Zeichenfolgendarstellung haben, sodass sie als .csvDateien gespeichert werden können. Das Laden des .csvwird dann diese Zeichenfolgendarstellung ergeben.

Wenn Sie die tatsächlichen Objekte speichern möchten, sollten Sie verwenden DataFrame.to_pickle()(Hinweis: Objekte müssen auswählbar sein!).

Um Ihre zweite Frage zu beantworten, können Sie sie zurückkonvertieren mit ast.literal_eval:

>>> from ast import literal_eval
>>> literal_eval('[1.23, 2.34]')
[1.23, 2.34]
anon582847382
quelle
1
Pandas Datenrahmen unterstützen das Speichern von willkürlichen Objekten, so dass dies hätte funktionieren sollen
EdChum
3
@EdChum Offenbar nicht, denn ich habe listumgewandelt stringmit to_csvgefolgt von from_csvin Version 0.17.1.
James Hirschorn
Obwohl dies als die richtige Antwort angegeben wird, bevorzuge ich die unten stehende Lösung von @markroxor, bei der Pandas direkt beim Import verwendet werden, um dieses Problem zu lösen, ohne eine andere externe Bibliothek zu laden.
Wissensdurst
32

Sie können Pandas direkt verwenden -
df = pd.read_csv(df_name, converters={'column_name': eval})

Dadurch wird diese Spalte als entsprechender D-Typ in Python anstelle einer Zeichenfolge gelesen.

Markroxor
quelle
4
Ich habe überall gesucht und das ist es, wonach ich gesucht habe. Vielen Dank.
AlanPear
2
Ich hatte das gleiche Problem und diese Antwort löste den Grund und nicht die Symptome, weshalb ich dafür gestimmt habe
AHR
2
Dies ist die richtige Antwort, da dadurch der Import einer anderen Bibliothek vermieden wird.
Wissensdurst
8

Ich bin gerade auf dieses Problem gestoßen und es gibt eine sehr einfache Lösung ( pandas.eval () ). Ich benutze 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'])
elPastor
quelle
3

1) Es gibt einen Weg, um dieses Verhalten zu umgehen. Verwenden Sie loc hilft hier.

>>> 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) Pythonische Methode zum Konvertieren dieser Zeichenfolge in eine Liste. (Dies ist wahrscheinlich das, was Sie möchten, da der von Ihnen verwendete DataFrame gespeichert und aus einem CSV-Format geladen wurde. Hierfür gibt es einige Lösungen.) Dies ist eine Ergänzung zur Antwort von 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]
Michael James Kali Galarnyk
quelle
2
Beachten Sie, dass Sie keine Lambda-Funktion verwenden müssen. Die applyMethode verwendet die Eingabe jeder Zeile innerhalb der Funktion, die Sie übergeben. Schreiben Sie einfach .apply(literal_eval). Reservieren Sie das Lambda für eine kompliziertere Logik.
Kevin Glynn
df.list = df.list.apply(literal_eval)
Trenton McKinney
2

Ich hatte das gleiche Problem. Beim Speichern einer Datenrahmen-Listenspalte in einer CSV-Datei mit df.to_csv () werden Listenspalten in eine Zeichenfolge konvertiert, z. B. "[42, 42, 42]" anstelle von [42, 42, 42].

Die Antwort von Alex ist korrekt und Sie können literal_evaldie Zeichenfolge wieder in eine Liste konvertieren. Das Problem bei diesem Ansatz besteht darin, dass Sie eine zusätzliche Bibliothek importieren und die Funktion anwenden oder Ihrem Datenrahmen zuordnen müssen. Der einfachere Weg ist, Pandas zu zwingen, die Spalte als Python-Objekt (dtype) zu lesen.

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

Das O wird für Python-Objekte einschließlich Listen verwendet. Mehr Infos hier . Bitte beachten Sie, dass diese Methode fehlschlägt, wenn Sie leere Listenzeichenfolgen analysieren: "[]"

Alternativ können Sie auch eine Funktion auf Ihre Spalte anwenden (diese ist für ganze Zahlen):

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))
Rutger Hofste
quelle
1

Nur als Referenz ... Pandas konvertieren keine Listen in Zeichenfolgen. ..

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'
namit
quelle
2
Wie ich festgestellt habe, konvertieren Pandas manchmal eine Liste in eine Zeichenfolge. Es muss damit zu tun haben, wie ich diesen DataFrame definiere oder Daten in ihn einfüge. Gut zu wissen, um später darauf zurückgreifen zu können.
Gyan Veda
Ich kann dieses Problem nicht neu erstellen
user1827356
@ user1827356, ich habe es herausgefunden! Ich werde meine Frage jetzt bearbeiten.
Gyan Veda
1
  • Verwenden Sie ast.literal_evaldiese Option, um eine Zeichenfolge mit einem Python-Literal oder einer Containeranzeige sicher auszuwerten.
  • Konvertieren Sie die Spalte beim Lesen der Datei mithilfe des convertersParameters von pandas.read_csv.

Daten in test.csv

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

Konvertieren Sie die Spalte beim Erstellen der 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

Konvertieren Sie die Spalte eines vorhandenen Datenrahmens

df.col1 = df.col1.apply(literal_eval)
Trenton McKinney
quelle
0

Ein einfacher Hack, den ich verwendet habe, besteht darin, eine Lambda-Funktion aufzurufen, die das erste und das letzte Element (die Listenklammern in str-Form) indiziert und die Split-Methode aufruft, gefolgt von einer anderen, die die Listenelemente durch Ints ersetzt.

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

Hinzufügen zu Alex ' Antwort. Hier ist eine andere Version, mit der einzelne Elemente von einer Zeichenfolge in eine Liste konvertiert werden können

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))
John Doe
quelle