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.
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
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;
Hallo @mfab,
danke für die superschnelle Antwort
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.
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
Danke euch beiden für die schnelle und kompetente Hilfe
@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
@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!
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;
hallo Michael,
die Vorarbeit von mfab nutzend 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
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
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;
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!