LesezeichenAbonnierenRSS-Feed abonnieren
MichaelSch
Calcite | Level 5

Hallo zusammen,

 

folgendens Problem:

Ich möchte eine .txt-Datei spaltenorientiert einlesen. Die Besonderheit dabei ist, dass die Datei zuerst eine ID enthält, anschließend jeweils immer drei zusammengehörende Felder die jedoch nicht unbedingt befüllt sein müssen (Siehe Beispiel unten). Es können also Spalten für drei Personen (bei drei Spalten pro Person also neun Spalten) vorhanden sein, jedoch nur für zwei Personen ausgefüllt sein (also nur sechs Spalten).

Die Datei soll nun so importiert werden, dass die Personen jeweils untereinander stehen und am Anfang die ID-Spalte steht (Sprich, die IDs kommen dann mehrfach in der neuen Liste vor). Falls eine Person keine Einträge hat, soll diese nicht mit importiert werden, um beim obigen Beispiel zu bleiben, sollen hier also nur zwei Beobachtungen angelegt werden.

 

Hier der Aufbau:

 

ID  Data1_Person1  Data2_Person1  Data3_Person1   Data1_Person2  Data2_Person2  Data3_Person2   Data1_Person3  Data2_Person3  Data3_Person3

 

Ich suche schon tagelang nach einer Lösung und wäre über eine Hilfe sehr dankbar.

10 ANTWORTEN 10
mfab
Quartz | Level 8

Hallo @MichaelSch,

 

vielleicht könntest Du das Ganze noch etwas genauer beschreiben.

 

Ich lese zwei Themen aus Deinem Text. Zum Einen das Einlesen der Textdatei, in der ggf. Felder nicht befüllt sind und zum Anderen das Transformieren der Daten in ein gewünschtes Format.

Wo liegt nun die Frage; beim Einlesen oder beim Weiterverarbeiten? (Natürlich lässt sich das ggf. in einem Schritt erledigen, allerdings halte ich hier die Trennung für sinnvoll, um das Problem zu erkennen)

 

Vielleicht kannst Du Dein Beispiel noch etwas ergänzen; z.B. mit einer Tabelle und einigen Beispielsätzen, die geliefert werden, sowie einer Darstellung des gewünschten Ergebnisses.

 

Herzliche Grüße

Michael

mfab
Quartz | Level 8

Trifft dieses Beispiel das Problem?

 

Statt 3 Spalten pro Person habe ich das auf je eine Spalte reduziert.

 

data test;
id = 1; dat1 = 'd1'; dat2 = 'd2'; dat3 = 'd3'; output;
id = 2; dat1 = 'd1'; dat2 = 'd2'; dat3 = 'd3'; output;
id = 3; dat1 = 'd1'; dat2 = 'd2'; dat3 = ''; output;
id = 4; dat1 = 'd1'; dat2 = 'd2'; dat3 = ''; output;
id = 5; dat1 = 'd1'; dat2 = ''; dat3 = 'd3'; output;
id = 6; dat1 = ''; dat2 = ''; dat3 = ''; output;
id = 7; dat1 = ''; dat2 = ''; dat3 = 'd3'; output;
id = 8; dat1 = 'd1'; dat2 = 'd2'; dat3 = 'd3'; output;
run;

proc transpose data=test out=test2 (where=(col1 NE ''));
by id;
var dat1 dat2 dat3;
run;

 

MichaelSch
Calcite | Level 5

Hallo @mfab,

 

danke für die superschnelle Antwort Smiley (fröhlich)

 

Im Anhang habe ich eine Beispieldatei angefügt, die zwei Reiter enthält, einmal die Ausgangsdaten und die Ergebnistabelle. Die Besonderheit dabei ist auch noch, dass es eingentlich zwei IDs gibt. Ich hoffe die Datei ist möglichst selbsterklärend, ansonsten einfach fragen.

 

In wievielen Schritten der Import passieren soll ist egal.

 

Vielleicht ist es irgendwie über Makros oder Schleifen möglich, da die Anzahl der Daten relativ groß ist (es sind 100 Personen) und noch sehr viel mehr Datensätze.

 

Wie bereits geschrieben, sollen nur die Personen Importiert werden, für die mindestens ein Datenfeld befüllt ist.

mfab
Quartz | Level 8

alles klar, schau mal hier:

 

%let num_pers = 3;

%macro testme;

data work.test (keep=id1 id2 person data1 data2 data3);

  INFORMAT
    id1    best.
    id2    best.
    %do i = 1 %to &num_pers.;
        data1_person&i. best.
        data2_person&i. best.
        data3_person&i. $char3.
    %end;
  ;

  INFILE "/home/user/Beispiel.csv"
/*    LRECL = 123*/
    DLM=';'
    TRUNCOVER
    FIRSTOBS=2
    DSD ;

  INPUT
    id1
    id2
     %do i = 1 %to &num_pers.;
        data1_person&i.
        data2_person&i.
        data3_person&i.
    %end;
  ;

  %do i = 1 %to &num_pers.;
     person = &i.;
     data1 = data1_person&i.;
     data2 = data2_person&i.;
     data3 = data3_person&i.;
     if data1 ne . or data2 ne . or data3 ne '' then OUTPUT;
  %end;
run;

%mend;

%testme

Vorausgesetzt in dem Fall, dass bekannt ist, wieviele Personen in der Datei geliefert werden. Falls das nicht bekannt ist, könnte man das auch noch irgendwie lösen, indem man die erste Zeile einliest und die Anzahl der Variablen ermittelt. Wenn bekannt ist, dass immer 3 Datensätze pro Person kommen, kann man dann die Anzahl ermitteln.

Wichtig ist sicherlich die breite der Datei, bzw. eines Satzes im Auge zu behalten und ggf. mit LRECL zu arbeiten.

 

Wenn sich die Rahmenbedingungen ändern ließen, wäre bestimmt der Code auch noch schöner zu schreiben ("Person1_Data1" ist m.E. schöner zu handhaben als "Data1_Person1").

Dazu sei gesagt, dass mein Beispiel oben auch eher ein Lösungsansatz darstellt, als die schönste Lösung zu präsentieren. 😉

Mein Ansatz ist der, die Schreibarbeit für alle Variablen durch ein Macro machen zu lassen. Sicherlich wäre auch eine Variante mit Arrays denkbar.

 

Herzliche Grüße

Michael

MichaelSch
Calcite | Level 5

Danke euch beiden für die schnelle und kompetente Hilfe Smiley (überglücklich)

 

@mfab deinem Tipp für die Änderung stimme ich zu, der Aufbau Person1_Data1 ist besser, hab ich jetzt geändert.

 

Die Anzahl der jeweiligen Personen pro Datensatz ist leider nicht bekannt. Könntest du mir hier evtl. noch etwas mit einbauen?

 

Es sind bis zu 100 Personen und jede Person hat immer genau 3 Datensätze (einzelne Datensätze können auch mal leer sein).

Hier die Größe der Felder:

ID1: 20 Zeichen, ID2: 2 Zeichen, Data1: 1 Zeichen, Data2: 1 Zeichen, Data3: 3 Zeichen

 

Vielen Dank im Voraus Smiley (fröhlich)

mfab
Quartz | Level 8

@MichaelSch: Um die Anzahl der Variablen zu bestimmen bietet sich z.B. folgender Schritt an:

 

data _null_;

  INFILE "/home/user/Beispiel.csv"
    LRECL = 4008 /* Länge d. Variablenbezeichnung ID1 und ID2 (6) plus Länge d. Variablenbezeichnung d. Personendaten (13) mal max. Anzahl (100) mal je drei Felder (3) plus maximale Anzahl Trennzeichen (102)
                    6 + (13*100*3) + 102 */
    DLM=';'
    TRUNCOVER
    DSD ;

  INPUT lesestring $char4008.; /* Berechnung Länge s.o. */

  /* Anzahl Variablen zählen und ID1, ID2 abziehen; durch 3 teilen, da immer 3 Datenfelder pro Person kommen */
  /* hier könnte zur Sicherheit noch ein umfassendes "INT()" ergänzt werden, falls mal nicht 3 Datenfelder pro Person vorhanden sind */
  anzahl = divide(sum(countw(lesestring),-2),3);
 
  call symput('num_pers', strip(anzahl)); /* Macrovariable für späteres Macro befüllen */
  stop;
run;

 

Voraussetzung hier: Wir wissen, dass maximal 100 Personen enthalten sein können, andernfalls müsste man die Längen (LRECL und Länge des String) anpassen.

Ich habe jetzt kein entsprechendes File zur Hand, ich würde das aber auf jeden Fall mal mit einem maximal breiten File testen.

 

Die Variablen habe ich nicht entsprechend angepasst - da weißt Du wahrscheinlich am Besten, ob in den Daten Strings vorkommen können oder ob alles numerisch ist.

 

Viel Erfolg!

Patrick
Opal | Level 21

@MichaelSch

Das foldgende Programm sollte genuegen:

filename have 'c:\temp\Beispiel.csv' lrecl=1000;

data want;
  infile have dsd dlm=',' truncover firstobs=2 length=_len col=_col;
  input id1:$20. id2:$2. @;

  do while(_col<_len);
    input Data1_Person:$1. Data2_Person:$1. Data3_Person:$3. @;
    if cmiss(Data1_Person, Data2_Person, Data3_Person)<3 then output;
  end;
run;
HeideTribius
Fluorite | Level 6

hallo Michael,

 

die Vorarbeit von mfab nutzend Smiley (fröhlich) und auch nur eine Variable pro Person:

data test;
      id = 1; dat1 = 'd1'; dat2 = 'd2'; dat3 = 'd3'; output;
      id = 2; dat1 = 'd1'; dat2 = 'd2'; dat3 = 'd3'; output;
      id = 3; dat1 = 'd1'; dat2 = 'd2'; dat3 = '';   output;
      id = 4; dat1 = 'd1'; dat2 = 'd2'; dat3 = '';   output;
      id = 5; dat1 = 'd1'; dat2 = '';   dat3 = 'd3'; output;
      id = 6; dat1 = '';   dat2 = '';   dat3 = '';   output;
      id = 7; dat1 = '';   dat2 = '';   dat3 = 'd3'; output;
      id = 8; dat1 = 'd1'; dat2 = 'd2'; dat3 = 'd3'; output;
run;

data daten(keep=id name);
      set test (where=(name) rename=(dat1=name))
          test (where=(name) rename=(dat2=name))
          test (where=(name) rename=(dat3=name));
run;

 

 

viele Grüße. Heide

HeideTribius
Fluorite | Level 6

hallo Michael,

 

hier noch mal komplett, mit Person und allen Variablen:

 

data rohdaten;
   id1= 12345; id2= 1; Data1_Person1= 1; Data2_Person1= 2; Data3_Person1= 53; 
Data1_Person2= 3; Data2_Person2= .; Data3_Person2= 55;
Data1_Person3= 2; Data2_Person3= 2; Data3_Person3= 12; output; id1= 12345; id2= 2; Data1_Person1= 2; Data2_Person1= 1; Data3_Person1= 62;
                       Data1_Person2= 3; Data2_Person2= 1; Data3_Person2= 71;
                       Data1_Person3= .; Data2_Person3= .; Data3_Person3= .; output; id1= 12346; id2= 1; Data1_Person1= 2; Data2_Person1= 55; Data3_Person1= .;
                       Data1_Person2= .; Data2_Person2= .; Data3_Person2= .;
                       Data1_Person3= .; Data2_Person3= .; Data3_Person3= .; output; id1= 12347; id2= 1; Data1_Person1= 3; Data2_Person1= 1; Data3_Person1= 85;
                       Data1_Person2= 1; Data2_Person2= 2; Data3_Person2= 33;
                       Data1_Person3= .; Data2_Person3= .; Data3_Person3= .; output; id1= 12347; id2= 2; Data1_Person1= 2; Data2_Person1= 2; Data3_Person1= 43;
                       Data1_Person2= 2; Data2_Person2= 1; Data3_Person2= 13;
                       Data1_Person3= .; Data2_Person3= .; Data3_Person3= .; output; id1= 12347; id2= 3; Data1_Person1= 1; Data2_Person1= 2; Data3_Person1= 25;
                       Data1_Person2= 1; Data2_Person2= 2; Data3_Person2= 29;
                       Data1_Person3= 1; Data2_Person3= 42; Data3_Person3= .; output; id1= 12348; id2= 1; Data1_Person1= .; Data2_Person1= .; Data3_Person1= .;
                       Data1_Person2= .; Data2_Person2= .; Data3_Person2= .;
                       Data1_Person3= .; Data2_Person3= .; Data3_Person3= .; output; id1= 12349; id2= 1; Data1_Person1= 2; Data2_Person1= 1; Data3_Person1= 25;
                       Data1_Person2= 3; Data2_Person2= 1; Data3_Person2= 87;
                       Data1_Person3= 1; Data2_Person3= 1; Data3_Person3= 58; output; run; data daten(keep=id1 id2 person data1 data2 data3); length id1 id2 person 8; set rohdaten (where=(data1 or data2 or data3)
rename=(Data1_Person1=data1 Data2_Person1=data2 Data3_Person1=data3)
in=a) rohdaten (where=(data1 or data2 or data3)
rename=(Data1_Person2=data1 Data2_Person2=data2 Data3_Person2=data3)
in=b) rohdaten (where=(data1 or data2 or data3)
rename=(Data1_Person3=data1 Data2_Person3=data2 Data3_Person3=data3)
in=c); if a then person=1; if b then person=2; if c then person=3; run; proc sort data=daten; by id1 id2 person; run;

 

viele Grüße. Heide

jh_ti_bw
Obsidian | Level 7
filename d "C:\temp\Kopie von Beispiel.csv";
Proc import 
  datafile = d
  out      = d
  dbms     = dlm
  replace
;
  guessingrows=max;
  delimiter   =";";
run;
Proc sort data = d ; by id1 id2; run;
Proc transpose 
  data    = d
  out     = d_t (
    where = (lowcase(variables) not in ('id1', 'id2') and spalte1 is not null )
  )
  name    = Variables
  prefix  = spalte
;
  by id1 id2;
  var _all_;
run;
Data d_t1;
  set d_t;
  Person    = compress(scan(variables,2,'_'),'','dk');
  Variables = scan(variables,1,'_');
run;
Proc transpose data = d_t1 out=fertig(drop=_:);
  by id1 id2 person;
  var spalte1;
  id variables;
run; 

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

Diskussionsstatistiken
  • 10 Antworten
  • 4093 Aufrufe
  • 1 Kudo
  • 5 in Unterhaltung