2017-09-15 3 views
1

Ich habe eine recurring problem mit Speichern von großen Zahlen in Python zu CSV. Die Zahlen sind Millisekunden-Epochenzeitstempel, die ich nicht konvertieren oder abschneiden kann und die in diesem Format gespeichert werden müssen. Da die Spalten mit den Millisekunden-Zeitstempeln auch einige NaN-Werte enthalten, wirft Pandas diese automatisch auf float (siehe the documentation in den Gotchas unter "Unterstützung für ganzzahlige NA".Python pandas große schwimmt mit to_csv

Ich kann dieses Verhalten anscheinend nicht vermeiden, also meine Frage ist, Wie kann ich diese Zahlen als Integer-Wert speichern, wenn ich df.to_csv verwende, dh ohne Dezimalpunkt oder nachgestellte Nullen? Ich habe Spalten mit Zahlen unterschiedlicher Gleitkommazahl im selben Datenrahmen und möchte die Information dort nicht verlieren die float_format Parameter in to_csv scheinen das gleiche Format für all Schwimmer Spalten in meinem Datenrahmen anzuwenden

Ein Beispiel:.

>>> df = pd.DataFrame({'a':[1.25, 2.54], 'b':[1424380449437, 1425510731187]}) 
>>> df['b'].dtype 
Out[1]: dtype('int64') 
>>> df.loc[2] = np.NaN 
>>> df 
Out[1]: 
     a    b 
0 1.25 1.424380e+12 
1 2.54 1.425511e+12 
2 NaN   NaN 
>>> df['b'].dtype 
dtype('float64') 
>>> df.to_csv('test.csv') 
>>> with open ('test.csv') as f: 
...  for line in f: 
...   print(line) 
,a,b 
0,1.25,1.42438044944e+12 
1,2.54,1.42551073119e+12 
2,, 

Wie Sie sehen können, habe ich die Genauigkeit der letzten beiden Ziffern meines Zeitstempels verloren.

+0

Sie könnten die 'nan' Werte möglicherweise durch Null ersetzen und dann die Spalte in intergers umwandeln. 'df.b = df.b.fillna (0) .astype (int)' oder '-1' verwenden, um die Einträge in der späteren Verarbeitung zu identifizieren. –

+0

Das ist eine Möglichkeit, aber ein ziemlich klobiger Workaround. Ich würde es vorziehen, die "Nan" -Werte als solche zu behalten, da sie eine einfache Indizierung und Filterung ermöglicht. Außerdem kann jeder Platzhalterwert, den ich für das "nan" verwende, im Dataframe natürlich auftreten. – Alarik

Antwort

1

Während pd.to_csv keinen Parameter hat, um das Format einzelner Spalten zu ändern, funktioniert pd.to_string. Es ist ein wenig umständlich und könnte ein Problem für sehr große DataFrames sein, aber Sie können es verwenden, um eine richtig formatierte Zeichenfolge zu erzeugen und dann diese Zeichenfolge in eine Datei schreiben (wie in dieser answer zu einer ähnlichen Frage vorgeschlagen). to_string 's formatters Parameter nimmt zum Beispiel ein Wörterbuch von Funktionen zum Formatieren einzelner Spalten. In Ihrem Fall könnten Sie Ihren eigenen benutzerdefinierten Formatierer für die Spalte "b" schreiben und die Standardeinstellungen für die anderen Spalten beibehalten.Diese Formatierer aussehen könnte etwas wie folgt aus:

def printInt(b): 
    if pd.isnull(b): 
     return "NaN" 
    else: 
     return "{:d}".format(int(b)) 

Jetzt können Sie diese Zeichenfolge zu erzeugen, verwenden:

df.to_string(formatters={"b": printInt}, na_rep="NaN") 

die gibt:

'  a    b\n0 1.25 1424380449437\n1 2.54 1425510731187\n2 NaN   NaN' 

Sie können sehen, dass es immer noch die Problem, dass dies nicht durch Komma getrennt ist und to_string hat eigentlich keinen Parameter, um ein benutzerdefiniertes Trennzeichen festzulegen, aber das kann leicht durch eine Regex behoben werden:

import re 
re.sub("[ \t]+(NaN)?", ",", 
     df.to_string(formatters={"b": printInt}, na_rep="NaN")) 

gibt:

with open("/tmp/test.csv", "w") as f: 
    print(re.sub("[ \t]+(NaN)?", ",", 
       df.to_string(formatters={"b": printInt}, na_rep="NaN")), 
      file=f) 

was dazu führt, was man wollte:

',a,b\n0,1.25,1424380449437\n1,2.54,1425510731187\n2,,' 

Dies kann nun in die Datei geschrieben werden

,a,b 
0,1.25,1424380449437 
1,2.54,1425510731187 
2,, 

Wenn Sie wollen die NaN ‚s in der CSV-Datei zu halten, die Sie gerade die Regex ändern können:

with open("/tmp/test.csv", "w") as f: 
    print(re.sub("[ \t]+", ",", 
       df.to_string(formatters={"b": printInt}, na_rep="NaN")), 
      file=f) 

geben:

vor
,a,b 
0,1.25,1424380449437 
1,2.54,1425510731187 
2,NaN,NaN 

Wenn Ihr Datenrahmen mit Leerzeichen enthalten Strings, Eine robuste Lösung ist nicht so einfach. Sie könnten vor jedem Wert ein anderes Zeichen einfügen, das den Beginn des nächsten Eintrags anzeigt. Wenn Sie in allen Zeichenfolgen nur einzelne Leerzeichen haben, können Sie beispielsweise ein anderes Leerzeichen verwenden. Dies würde den Code dies ändern:

import pandas as pd 
import numpy as np 
import re 

df = pd.DataFrame({'a a':[1.25, 2.54], 'b':[1424380449437, 1425510731187]}) 
df.loc[2] = np.NaN 

def printInt(b): 
    if pd.isnull(b): 
     return " NaN" 
    else: 
     return " {:d}".format(int(b)) 

def printFloat(a): 
    if pd.isnull(a): 
     return " NaN" 
    else: 
     return " {}".format(a) 

with open("/tmp/test.csv", "w") as f: 
    print(re.sub("[ \t][ \t]+", ",", 
       df.to_string(formatters={"a": printFloat, "b": printInt}, 
           na_rep="NaN", col_space=2)), 
      file=f) 

, die geben würde:

,a a,b 
0,1.25,1424380449437 
1,2.54,1425510731187 
2,NaN,NaN 
+0

Es tut mir leid, das Beispiel am Ende ist nicht das, was ich wollte, aber was ich vermeiden wollte. Also sollten NaN's als solche dargestellt werden. Wie kann ich die Regex meine Spaltennamen, die aus mehr als einem Wort bestehen, nicht aufteilen? – Alarik

+0

Ich habe meine Antwort aktualisiert, um Whitespaces in Spaltennamen zuzulassen und die NaNs zu behalten (ich dachte, du wolltest nur, dass diese in 'df' bleiben). Hilft das? – jotasi

+0

Ich habe Ihre Antwort akzeptiert, weil sie mein Problem löst, obwohl es eine entsetzliche Lösung bleibt, vor allem, weil ich die Formatierer für ein paar Dutzend Spalten angeben musste;) Ich wählte die Regex-Übereinstimmung für einzelne Abstände, da der doppelte Abstand in Schwierigkeiten geraten würde mit der Funktion 'to_string' von Pandas, wenn Spaltennamen oder Zelleneinträge zu lang waren (da es nur ein einzelnes Leerzeichen in der Spalte hinterlassen würde). Danke für Ihre Hilfe! – Alarik

1

dies funktionieren könnte Vielleicht:

pd.set_option('precision',15) 
df = pd.DataFrame({'a':[1.25, 2.54], 'b':[1424380449437, 1425510731187]}) 
fg = df.applymap(lambda x: str(x)) 
fg.loc[2] = np.NaN 
fg.to_csv('test.csv', na_rep='NaN') 

Ihre Ausgabe in etwa so sein sollte (ich bin auf ein Mac):

enter image description here

+0

Aber in diesem Fall müssen Sie die DataFrames-Einträge in Zeichenfolgen ändern, bevor Sie 'NaN's einfügen, oder? – jotasi

+2

@jotasi ja, sonst verlierst du die Präzision. Ich dachte, es könnte eine Alternative zu deiner sein – erasmortg

+0

Vielen Dank für Ihre Eingabe, aber das funktioniert nicht für mich - mein ursprünglicher Datenrahmen hat die NaNs die ganze Zeit, ich füge sie erst nach dem Beispiel hinzu, um zu zeigen, wie die Spalten kommen Kraft gegossen um zu schweben. – Alarik

Verwandte Themen