2017-06-29 1 views
0

Je souhaite lire le premier et le dernier enregistrement d'un grand nombre de fichiers .csv (plusieurs gigaoctets) stockés dans un dossier sur une machine Linux. Supposons qu'ils s'appellent have1.csv, have2.csv, ... et ainsi de suite.SAS - Lecture de la première et dernière observation de plusieurs fichiers csv

J'ai donc essayé le code suivant, qui ne me donne que la première ligne. Mais pas la dernière ligne.

%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; 

J'apprécierais beaucoup votre aide! Si j'ai mal compris le concept ou l'interaction entre infile, fin, eov, eof et input @, dites-moi s'il vous plaît! Je ne sais pas où mon erreur est ...

+1

Etes-vous également en train d'ignorer les lignes d'en-tête? Est-ce cela le commentaire sur FIRSTOBS = option? – Tom

+0

Oui, désolé de ne pas avoir répondu plus tôt. –

Répondre

1

Si vous voulez utiliser des caractères génériques dans l'instruction INFILE vous pouvez utiliser l'EOV = option créer une variable qui marquera quand un nouveau fichier commence. Notez que vous devez réinitialiser manuellement le drapeau EOV.

Lisez et maintenez la ligne avant de lire les valeurs afin de pouvoir tester si un nouveau fichier a démarré. De cette façon, vous pouvez sortir la dernière ligne du fichier précédent. Vous devrez également RETENIR vos variables d'entrée afin que les valeurs de la dernière ligne du fichier précédent soient disponibles.

Vous devez également utiliser l'option END = pour pouvoir afficher la dernière ligne du dernier fichier.

Exemple:

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; 

sortie Exemple:

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 

Si vous voulez sauter la première ligne de chaque fichier (la ligne d'en-tête) ajouter cette déclaration juste après la déclaration input @;.

if _n_=1 or eov then input; 

Notez que vous devrez ajuster la logique s'il est possible que vos fichiers d'entrée n'ont pas tous au moins deux lignes de données (trois lignes de comptage ligne d'en-tête).

+1

Ceci est une excellente réponse, dans le cas où j'ai eu des fichiers CSV qui ne contiennent pas une ligne d'en-tête avec les noms de variables. Malheureusement, le mien a ... désolé de ne pas être assez clair. Mais: existe-t-il un moyen d'ignorer la première observation dans chaque fichier afin que le PDV ne reçoive pas l'entrée de l'information d'en-tête dans la première ligne? Dans ce cas, je pense que votre solution avec l'instruction retain fonctionnerait réellement ... –

+0

end = les options semblent seulement attraper la dernière ligne du dernier fichier. De toute façon tu as prouvé qu'il n'y avait pas de boucle nécessaire, merci. – vasja

+0

Il n'est pas difficile d'ignorer les lignes d'en-tête. Utilisez le drapeau EOV pour savoir quand vous devez sauter. – Tom

1

Cela semble fonctionner pour moi, s'il vous plaît essayer:

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; 

En fait, comme Tom décrit ci-dessous, il n'y a pas besoin de while (chose dangereuse: -)). J'ai modifié le code maintenant: (besoin d'ajouter Conserve parce que nous sommes en boucle à l'étape de données lui-même)

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

Cela fonctionne, très bien.Le truc avec l'entrée de chaque ligne, mais seulement la sortie du dernier, ne me venait pas à l'esprit ... bien fait! Merci! : D –

+0

Désolé. Je ne peux pas accorder deux réponses. Tom a raison. Son code est un peu plus clair. Mais le vôtre fonctionne bien aussi. Je suis déchiré ... Merci pour votre aide! : D –

1

Si vous avez la liste des fichiers, le code est plus clair. Par exemple, si vous pouvez utiliser le moteur PIPE, vous pouvez utiliser la commande ls (ou Dir) pour obtenir les noms de fichiers. Utilisez ensuite l'option FILEVAR = pour lire dynamiquement chaque fichier individuel.

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; 

Ou si vos fichiers sont volumineux, vous pouvez profiter d'utiliser PIPE pour utiliser les commandes head et tail pour trouver le début et la fin de chaque fichier sans avoir besoin de SAS lire le fichier entier. Vous aurez probablement besoin de tester pour voir si elle a réellement amélioré la performance.

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

La solution tête et queue n'est pas plus rapide. Bien plus lent. J'ai dû l'ajuster avec une commande find "" cd ~/thepath; find.type -f -name "" * .csv "" -print "' pour générer la liste ... –