Ich suche eine große CSV in einem Datenrahmen mit der zusätzlichen Einschränkung, die ich fehlschlagen möchte früh wenn bestimmte Spalten fehlen (da die Eingabe nicht wie erwartet ist), aber ich wollen alle die Spalten, nicht nur die erforderlichen Spalten, in den Dataframe aufgenommen werden. In pandas.read_csv
, es scheint, dass ich das usecols
Argument verwenden kann, wenn ich die Teilmenge der Spalten zum Einlesen angeben möchte, aber die einzige offensichtliche Möglichkeit zu überprüfen, welche Spalten in einem Dataframe ich bin zu lesen ist zu lese eigentlich die Datei.Impose erforderliche Spalten Einschränkung in Pandas read_csv
ich eine Arbeits First-Pass-Version vorgenommen haben, die den Datenrahmen als Iterator liest, erhält die erste Zeile überprüft, dass die Spalten vorhanden sind, dann die Datei mit den normalen Argumente lautet:
import pandas as pd
from io import StringIO
class MissingColumnsError(ValueError):
pass
def cols_enforced_reader(*args, cols_must_exist=None, **kwargs):
if cols_must_exist is not None:
# Read the first line of the DataFrame and check the columns
new_kwargs = kwargs.copy()
new_kwargs['iterator'] = True
new_kwargs['chunksize'] = 1
if len(args):
filepath_or_buffer = args[0]
args = args[1:]
else:
filepath_or_buffer = new_kwargs.get('filepath_or_buffer', None)
df_iterator = pd.read_csv(filepath_or_buffer, *args, **new_kwargs)
c = next(df_iterator)
if not all(col in c.columns for col in cols_must_exist):
raise MissingColumnsError('Some required columns were missing!')
seek = getattr(filepath_or_buffer, 'seek', None)
if seek is not None:
if filepath_or_buffer.seekable():
filepath_or_buffer.seek(0)
return pd.read_csv(filepath_or_buffer, *args, **kwargs)
in_csv = """col1,col2,col3\n0,1,2\n3,4,5\n6,7,8"""
# Should succeed
df = cols_enforced_reader(StringIO(in_csv), cols_must_exist=['col1'])
print('First call succeeded as expected.')
# Should fail
try:
df = cols_enforced_reader(StringIO(in_csv), cols_must_exist=['col7'])
except MissingColumnsError:
print('Second call failed as expected.')
Diese fühlt sich ein bisschen chaotisch an und behandelt nicht wirklich alle möglichen Eingaben für filepath_or_buffer
(nicht suchbare Ströme zum Beispiel, oder Puffer, wo ich nicht bei 0 anfangen soll). Offensichtlich kann ich für den Moment das, was ich hier habe, an meinem spezifischen Anwendungsfall anpassen und bin damit fertig, aber ich frage mich, ob es eine elegantere Art gibt, dies zu tun (vorzugsweise nur Standard-Pandas-Funktionen), die in allgemein funktioniert .
Dies ist mehr oder weniger, was Ich mache in meiner Frage, aber es erfordert immer noch das Lesen der Datei zweimal, die nicht unbedingt nett mit CSVs, die Puffer und keine Pfade sind. Idealerweise würde ich etwas verwenden, das zuerst die Spalten liest, diese überprüft und dann entweder einen Fehler ausgibt oder wie erwartet weiterläuft. – Paul
Ich würde vorschlagen, Ihren Stream zu nehmen und ihn mit ['itertools.tee()'] (https://docs.python.org/3.6/library/itertools.html?highlight=tee#itertools.tee) zu duplizieren Ich glaube mich daran zu erinnern, dass Pandas damit nicht spielen. –
Ja, ich denke die Antwort zu diesem ist eigentlich "nein, es ist nicht möglich, es elegant zu machen". Die Version mit dem csvreader hat den Nachteil, dass sie alle Argumente von pandas.read_csv ignoriert. Ich denke, dass eine Kombination aus meiner Version und Ihrer ersten Version sowie eine größere Anzahl von Randüberprüfungen die einzige Möglichkeit sein könnten, eine generische Funktion zu erstellen, die dies tut. – Paul