2016-04-08 8 views
3

Gibt es eine pythonische Möglichkeit, um herauszufinden, welche Zeilen in einer CSV-Datei Header und Werte enthalten und welche Zeilen Papierkorb enthalten und dann die Header/Werte Zeilen in Datenrahmen?Python pandas: lesen csv mit mehreren Tabellen wiederholt Präambel

Ich bin relativ neu zu Python und habe es verwendet, um mehrere CSVs aus dem Datenprotokoll eines wissenschaftlichen Instruments zu lesen, und wenn CSVs bisher für andere Aufgaben zu behandeln, habe ich immer standardmäßig die pandas Bibliothek verwendet. Diese CSV-Exporte können jedoch abhängig von der Anzahl der "Tests" variieren, die an jedem Gerät protokolliert werden.

Die Spaltenüberschriften und die Datenstruktur sind zwischen den Geräten gleich, aber es gibt eine "Präambel", die jeden Test trennt, der sich ändern kann. So habe ich am Ende mit Backups auf, die wie folgt aussehen (in diesem Beispiel gibt es zwei Tests, aber es könnte sein, möglicherweise eine beliebige Anzahl von Tests):

blah blah here's a test and 
here's some information 
you don't care about 
even a little bit 
header1, header2, header3 
1, 2, 3 
4, 5, 6 

oh you have another test 
here's some more garbage 
that's different than the last one 
this should make 
life interesting 
header1, header2, header3 
7, 8, 9 
10, 11, 12 
13, 14, 15 

Wenn es sich um eine feste Länge Präambel jedes Mal war ich würde Verwenden Sie einfach den Parameter skiprow, aber die Präambel hat eine variable Länge und die Anzahl der Zeilen in jedem Test ist von variabler Länge.

Mein Endziel ist es, alle Tests verschmelzen und mit so etwas wie am Ende:

header1, header2, header3 
1, 2, 3 
4, 5, 6 
7, 8, 9 
10, 11, 12 
13, 14, 15 

Was ich kann dann mit Pandas wie gewohnt bearbeiten.

Ich habe das versucht, im Anschluss an die erste Reihe mit meinen erwarteten Header zu finden:

import csv 
import pandas as pd 

with open('my_file.csv', 'rb') as input_file:  
    for row_num, row in enumerate(csv.reader(input_file, delimiter=',')): 
     # The CSV module will return a blank list [] 
     # so added the len(row)>0 so it doesn't error out 
     # later when searching for a string 
     if len(row) > 0: 
      # There's probably a better way to find it, but I just convert 
      # the list to a string then search for the expected header 
      if "['header1', 'header2', 'header3']" in str(row): 
       header_row = row_num 

    df = pd.read_csv('my_file.csv', skiprows = header_row, header=0) 
    print df 

Dies funktioniert, wenn ich nur einen Test, weil es die erste Zeile findet, die Header, aber natürlich auch die header_row Variable wird immer jede zusätzliche Zeit aktualisiert es den Header findet, so in dem obigen Beispiel I mit Ausgang am Ende:

header1 header2 header3 
0  7   8   9 
1  10  11   12 
2  13  14   15 

ich bin verloren, herauszufinden, wie jede Instanz des Headers/Datensatz zu einem anhang Datenframe, bevor Sie weiter suchen g für die nächste Instanz des Headers/Datasets.

Und es ist wahrscheinlich nicht super effizient, wenn man mit einer großen Anzahl von Dateien zu tun, um es einmal mit dem csv Modul dann wieder mit pandas zu öffnen.

+0

Woher wissen Sie, was ist eine Überschrift und was ist Präambel? Wenn Kommas beginnen in der Präambel, dann sind Sie verloren ... – Chiel

+0

Nun, die Header sind mindestens Standard im Namen durch, so dass Sie wissen, dass die Header sind in welcher Zeile enthält 'header1, header2, header3' und die tatsächliche Datenmenge ist die Zeilen unmittelbar nach der Kopfzeile. Die Frage ist, wie man die Zeilen, die Header/Daten haben, aussortiert und in einen Datenrahmen bringt und den Rest ignoriert. –

+0

Vereinbart mit Chiel, die Kommata in der Einleitung sind das Risiko - wenn Sie wissen, dass Sie nicht die gleiche Anzahl von Kommas in jeder Zeile in der Präambel haben, die Sie in Ihren Daten haben, mit len ​​(Zeile), um sie zu filtern out ist einfach. –

Antwort

0

Dieses Programm könnte helfen. Es ist im Wesentlichen ein Wrapper um das Objekt csv.reader(), dessen Wrapper die guten Daten herausfiltert.

import pandas as pd 
import csv 
import sys 


def ignore_comments(fp, start_fn, end_fn, keep_initial): 
    state = 'keep' if keep_initial else 'start' 
    for line in fp: 
     if state == 'start' and start_fn(line): 
      state = 'keep' 
      yield line 
     elif state == 'keep': 
      if end_fn(line): 
       state = 'drop' 
      else: 
       yield line 
     elif state == 'drop': 
      if start_fn(line): 
       state = 'keep' 

if __name__ == "__main__": 

    df = open('x.in') 
    df = csv.reader(df, skipinitialspace=True) 
    df = ignore_comments(
     df, 
     lambda x: x == ['header1', 'header2', 'header3'], 
     lambda x: x == [], 
     False) 

    df = pd.read_csv(df, engine='python') 
    print df 
+0

Danke, Rob! Ich denke, das löst das Problem, aber ich bin neugierig ... setzt den Parameter "engine" in der Zeile 'df = pd.read_csv (df, engine = 'python')' '? –

0

Ja, es gibt eine pythonic Art und Weise, auf Pandas Basis zu tun, (dies eine schnelle Demonstration ist die Frage zu beantworten)

import pandas as pd 
from StringIO import StringIO 

#define an example to showcase the solution 
st = """blah blah here's a test and 
here's some information 
you don't care about 
even a little bit 
header1, header2, header3 
1, 2, 3 
4, 5, 6 

oh you have another test 
here's some more garbage 
that's different than the last one 
this should make 
life interesting 
header1, header2, header3 
7, 8, 9 
10, 11, 12 
13, 14, 15""" 

# 1- read the data with pd.read_csv 
# 2- specify that you want to drop bad lines, error_bad_lines=False 
# 3- The header has to be the first row of the file. Since this is not the case, let's manually define it with names=[...] and header=None.  
data = pd.read_csv(StringIO(st), delimiter=",", names=["header1","header2", "header3"], error_bad_lines=False, header=None) 

# the trash will be loaded as follows 
# blah blah here's a test and  NaN   NaN 
# let's drop these rows 
data = data.dropna() 

# remove the rows which contain "header1","header2", "header3" 
mask = data["header1"].str.contains('header*') 
data = data[~mask] 
print data 

Jetzt ist Ihr Datenrahmen sieht wie folgt aus:

header1 header2 header3 
5  1  2  3 
6  4  5  6 
13  7  8  9 
14  10  11 12 
15  13  14  15 
+0

Danke MedAli. Das mag eine andere Python-Anfängerfrage sein, aber was macht das "~" für Sie in "data = data [~ mask]"? Ich habe Probleme, eine Dokumentation darüber zu finden. –

+0

"~" bedeutet, die Werte innerhalb der Maske zu negieren (das ist ein np.Series halten Boolean wie (falsch, wahr, falsch, falsch ..). – MedAli

+0

@TheJ, ich hoffe, dies beantwortet Ihre Frage, wenn es, bitte Überlegen Sie, die Antwort zu akzeptieren (http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) – MedAli

Verwandte Themen