2017-06-29 2 views
0

Ich möchte den ersten und letzten Datensatz einer großen Anzahl von .csv-Dateien (mehrere Gigabyte) in einem Ordner auf einem Linux-Rechner gespeichert lesen. Angenommen, sie heißen have1.csv, have2.csv, ... und so weiter.SAS - Erste und letzte Beobachtung von mehreren CSV-Dateien lesen

Also habe ich den folgenden Code ausprobiert, der mir nur die erste Zeile gibt. Aber nicht die letzte Zeile.

%let datapath = ~/somefolder/;  
data want; 

length finame $300.; 
/*Reference all CSV files in input data folder*/ 
infile "&datapath.have*.csv" delimiter="," 
     MISSOVER DSD lrecl=32767 firstobs=2 
     eov=eov eof=eof filename=finame end=done; 

/*Define input format of variables*/ 
informat Var1 COMMA. Var2 COMMA. Var3 COMMA.; 
/*Loop over files*/ 
do while(not done); 

    /*Set trailing @ to hold the input open for the next input statement 
     this is because we have several files */ 
    input @; 

    /*If first line in file is encountered eov is set to 1, 
     however, we have firstobs=2, hence all lines would be skipped. 
     So we need to reset EOV to 0.*/ 
    if eov then 
    do; 
     /*Additional empty input statement 
     handles missing value at first loop*/ 
     input; 
     eov = 2; 
    end; 
    /*First observation*/ 
    if eov=2 then do; 
     input Var1--Var3; 
     fname=finame; 
     output; 
     eov = 0; 
    end; 

     /*Last observation*/ 
     if 0 then do; 
      eof:  input Var1--Var3; 
        fname=finame; 
        output; 
     end; 
     input; 

end; 
stop; 

run; 

Ich würde Ihre Hilfe sehr schätzen! Wenn ich das Konzept oder das Zusammenspiel von infile, end, eov, eof und input @ missverstanden habe, bitte sag es mir! Ich weiß nicht, wo mein Fehler ist ...

+1

Versuchen Sie auch Kopfzeilen zu überspringen? Ist das der Kommentar zu FIRSTOBS = Option? – Tom

+0

Ja, tut mir leid, dass ich nicht früher zurückgeschrieben habe. –

Antwort

1

Wenn Sie verwenden möchten Platzhalter in der INFILE-Anweisung Sie die EOV = Option verwenden können, um Erstellen Sie eine Variable, die anzeigt, wann eine neue Datei gestartet wird. Beachten Sie, dass Sie das EOV-Flag manuell zurücksetzen müssen.

Lesen und halten Sie die Zeile, bevor Sie die Werte lesen, damit Sie testen können, ob eine neue Datei gestartet wurde. Auf diese Weise können Sie die letzte Zeile der vorherigen Datei ausgeben. Außerdem müssen Sie Ihre Eingabevariablen beibehalten, damit die Werte aus der letzten Zeile der vorherigen Datei verfügbar sind.

Sie müssen auch die Option END = verwenden, um die letzte Zeile der letzten Datei ausgeben zu können.

Beispiel:

data want ; 
    retain filename str; 
    length fname filename $200 ; 
    infile '/dir1/file*' filename=fname eov=eov end=eof truncover ; 
    input @; 
    if eov then output; 
    filename=fname ; 
    input str $30. ; 
    if _n_=1 or eov or eof then output; 
    eov=0; 
run; 

Beispiel Ausgabe:

Obs filename  str 
1  /dir1/file1 Line1 
2  /dir1/file1 Line3 
3  /dir1/file2 Line1 
4  /dir1/file2 line4 
5  /dir1/file3 Line1 
6  /dir1/file3 Line3 

Wenn Sie erste Zeile jeder Datei (die Kopfzeile) fügen Sie diese Anweisung direkt nach der input @; Anweisung überspringen wollen.

if _n_=1 or eov then input; 

Hinweis müssen Sie die Logik ändern, wenn es möglich ist, dass Ihre Eingabedateien müssen nicht alle mindestens zwei Datenleitungen (drei Zeilen Kopfzeile gezählt).

+1

Das ist eine großartige Antwort, falls ich CSV-Dateien habe, die keine Kopfzeile mit den Variablennamen enthalten. Leider haben meine ... Entschuldigung, dass ich das nicht klar genug gemacht habe. Aber: Gibt es eine Möglichkeit, die erste Beobachtung in jeder Datei zu überspringen, so dass der PDV nicht die Eingabe von der Header-Information in der ersten Zeile erhält? In diesem Fall denke ich, dass Ihre Lösung mit der Retain-Anweisung funktionieren würde ... –

+0

end = Optionen scheint nur die letzte Zeile der letzten Datei zu fangen. Wie auch immer du bewiesen hast, dass keine While-Schleife benötigt wird, danke. – vasja

+0

Es ist nicht schwer, die Kopfzeilen zu überspringen. Verwenden Sie das EOV-Flag, um zu wissen, wann Sie übersprungen werden müssen. – Tom

1

Dies scheint für mich zu arbeiten, bitte versuchen Sie es:

data want; 

length finame $300.; 
/*Reference all CSV files in input data folder*/ 
infile "E:\temp\test\have*.txt" delimiter="," 
     MISSOVER DSD lrecl=32767 
     eov=eov filename=finame end=done; 

     /* Note: firstobs option seems to work on first file only */ 

/*Define input format of variables*/ 
informat Var1 COMMA. Var2 COMMA. Var3 COMMA.; 

input; /* skip header in first file */ 

input Var1--Var3; /* read first real record in first file */ 
fname=finame; 
output; 

/* Loop over files*/ 
do while(not done); 

    input @;/* try input do determine eov condition */ 

    if eov then do;/* new file detected - we're on header record, but variables contain values from previous record - see "read values" */ 
     output; /* variables contain values from previous record - output those values */ 
     input; /* skip header */ 
     eov = 0; 
     input Var1--Var3; /* read first real observation */ 
     fname=finame; 
     output; /* first line of new file */ 
    end; 

    input Var1--Var3; /* read values - it might be last record */ 
end; 
output; /* output last record of last file */ 
run; 

Eigentlich wie Tom unten beschrieben, gibt es keine Notwendigkeit für while-Schleife (gefährliche Sache: -)). Ich habe den Code jetzt geändert: (Bedarf hinzufügen Behält, weil wir in der Daten Schritt selbst sind Looping)

data want; 

length finame $300.; 
/*Reference all CSV files in input data folder*/ 
infile "E:\temp\test\have*.txt" delimiter="," 
     MISSOVER DSD lrecl=32767 
     eov=eov filename=finame end=done; 

informat Var1 COMMA. Var2 COMMA. Var3 COMMA.; 
retain Var1 Var2 Var3 fname; 
if _N_ = 1 then do; /* first file */ 
    input; /* skip header in first file */ 
    input Var1--Var3; /* read first real record in first file */ 
    fname=finame; 
    output; 
end; 

input @; /* try input do determine eov condition */ 

if eov then do; /* new file detected - we've moved past header record, but variables contain values from previous record - see "read values" */ 
    output; /* variables contain values from previous record - output those values */ 
    input; /* skip header */ 
    eov = 0; 
    input Var1--Var3; /* read first real observation */ 
    fname=finame; 
    output; /* first line of new file */ 
end; 
else input Var1--Var3; 
if done then output; 
run; 
+0

Das funktioniert, großartig.Der Trick mit der Eingabe jeder Zeile, aber nur die Ausgabe der letzten, kam mir nicht in den Sinn ... schön gemacht! Vielen Dank! : D –

+0

Entschuldigung. Ich kann keine zwei Antworten geben. Tom hat Recht. Sein Code ist ein wenig klarer. Aber deine Arbeit funktioniert auch gut. Ich bin zerrissen ... Danke für die großartige Hilfe, die ihr seid! : D –

1

Wenn Sie die Liste der Dateien haben, dann ist der Code klarer. Wenn Sie beispielsweise die PIPE-Engine verwenden, können Sie den Befehl ls (oder Dir) verwenden, um Dateinamen abzurufen. Verwenden Sie dann die Option FILEVAR =, um jede einzelne Datei dynamisch zu lesen.

data want ; 
    infile 'ls ~/test/dir1/file*' pipe truncover ; 
    input fname $200.; 
    filename=fname; 
    infile csv filevar=fname dsd truncover firstobs=2 end=eof ; 
    do _n_=1 by 1 while (not eof); 
    input str :$30. ; 
    if _N_=1 or eof then output; 
    end; 
run; 

Oder wenn Ihre Dateien sind groß Sie Vorteil der Verwendung von PIPE nehmen könnte head und tail Befehle zu verwenden, um den Anfang und das Ende jeder Datei zu finden, ohne dass SAS die gesamte Datei gelesen haben. Sie müssten wahrscheinlich testen, ob es tatsächlich die Leistung verbessert hat.

data want ; 
    infile 'ls ~/test/dir1/file*' pipe truncover ; 
    input filename $200.; 
    length cmd1 cmd2 $200 ; 
    cmd1='head -2 '||filename ; 
    infile top pipe filevar=cmd1 dsd truncover firstobs=2 end=eof1 ; 
    if (not eof1) then do; 
    input str :$30. ; 
    output; 
    end; 
    cmd2='tail -1 '||filename ; 
    infile bottom pipe filevar=cmd2 dsd truncover firstobs=1 end=eof2; 
    if (not eof2) then do; 
    input str :$30. ; 
    output; 
    end; 
run; 
+0

Die Kopf- und Schwanzlösung ist nicht schneller. Viel langsamer. Musste es mit einem Suchbefehl "cd ~/thepath; find. Type -f -name" "* .csv" "-print" 'anpassen, um die Liste zu erzeugen ... –

Verwandte Themen