4  Label- und Variablenfunktionen

Inspiriert von der UCLA einige Beispielstrings:

clear all
input str60 add
"4905 Lakeway Drive, College Station, Texas 77845 USA"
"673 Jasmine Street, Los Angeles, CA 90024"
"2376 First street, San Diego, CA 90126"
"6 West Central St, Tempe AZ 80068"
"1234 Main St. Cambridge, MA 01238-1234"
"Robert-Schuman-Platz 3, 53175 Bonn GERMANY"
"Regensburger Straße 100, 90478 Nürnberg Germany"
"  Ammerländer Heerstraße 114-118, 26129 Oldenburg GERMANY  "
end

4.1 Stringfunktionen

help string functions

gen x1 = substr(add,5,10)   //  substring von add -> Zeichen 5-10
gen x2 = wordcount(add)       // Worte zählen
gen x3 = word(add,5)      // 5. Wort
gen x4 = upper(add)         // alles groß
gen x5 = lower(add)     // alles klein
gen x6 = proper(add)    // jeweiles erster Buchstabe eines Wortes groß
gen x7 = trim(add)      // Leerzeichen am Ende und Beginn raus
gen x8 = strlen(add)    // Anzahl der Zeichen in add

Allerdings sind diese Funktionen auf ASCII-Zeichen ausgelegt - einfach gesagt: Umlaute, ß usw. bereiten Probleme. Daher gibt es eine Erweiterung mit usubstr, ustrupper(), ustrlower() usw. - mehr dazu jeweils in der Hilfe.

Der Weg zur passenden regex string-Funktion ist leider oft von vielen Versuchen begleitet, am einfachsten testet man mit display. So zeigt sich hier bspw., dass proper() hier “ß” und “ü” als Wortende/-beginn (mis-)versteht:

display proper("Regensburger Straße 100, 90478 nüRnberg germany")
Regensburger StraßE 100, 90478 NüRnberg Germany

…in der Hilfe finden wir ustrtitle:

display ustrtitle("Regensburger Straße 100, 90478 nüRnberg germany")
Regensburger Straße 100, 90478 Nürnberg Germany

Hier wenig hilfreich, aber häufig eine gute Abkürzung: split Mit parse() können wir einen Trenner angeben.

split add, parse(" ") gen(t)

Übung


clear all
input str1 x1
"2"
"3"
"5"
"23"
"21"
"2"
"--"
"2"
end

gen num = real(x1)
list
destring(x1), gen(num2)
            x1
  1. "2"
  2. "3"
  3. "5"
  4. "23"
  5. "21"
  6. "2"
  7. "--"
  8. "2"
  9. end

(1 missing value generated)

     +----------+
     | x1   num |
     |----------|
  1. |  2     2 |
  2. |  3     3 |
  3. |  5     5 |
  4. |  2     2 |
  5. |  2     2 |
     |----------|
  6. |  2     2 |
  7. |  -     . |
  8. |  2     2 |
     +----------+

x1: contains nonnumeric characters; no generate

4.2 Arbeiten mit “Regular Expressions”

“regular expressions” oder “regex” sind eine flexible Methode, strings (also Textinhalte) zu durchsuchen. Dabei suchen wir nach Mustern anstelle eines exakten matchings.

In Stata können wir regex für Variablen, aber auch für macros und Labels verwenden. regex sind aber nicht immer die (einfachste) Lösung und oft werden die Befehle sehr unübersichtlich. Daher hier nur ein kurzer Überblick.

Die drei Hauptfunktionen in Stata finden wir unter help regex:

regexm(s,re) allows you to search for the string described in your regular expressions. It evaluates to 1 if the string matches the expression.

regexs(n) returns the nth substring within an expression matched by regexm (hence, regexm must always be run before regexs).

regexr(s1,re,s2) searches for re within the string (s1) and replaces the matching portion with a new string (s2).

Allerdings sind diese Funktionen auf ASCII-Zeichen ausgelegt - einfach gesagt: Umlaute, ß usw. bereiten Probleme. Daher gibt es eine Erweiterung mit ustrregexm, ustrregexs, ustrregexrf und ustrregexra. Auf die konzentrieren wir uns hier.

4.2.1 regex (Grund-)Regeln

regex basieren auf einer Reihe an Zeichen, mit Hilfe derer wir strings durchsuchen können:

  • \d matcht eine Zahl
  • \w matcht “alphanumeric characters” (Buchstaben & Zahlen)
  • \s matcht ein Leerzeichen
  • ^ “matche Ausdruck am Anfang des strings”
  • $ “matche Ausdruck am Ende des strings”
  • - lässt eine range zu: a-z, 0-9, 5-8, F-M etc.
  • . bedeutet “matche irgendein Zeichen”
  • \ ist ein “escape character” für Zeichen, die ansonsten als regex-Operator verstanden würden
  • | oder-Operator
  • [] use in matching, such as [a-zA-Z0-9] for all alphanumeric characters
  • () darin angegebene Werte können wir mit ustrregexs extrahieren
  • * “matche 0 oder öfter” für den vorhergehenden Ausdruck
  • + “matche 1 oder öfter” für den vorhergehenden Ausdruck

4.2.2 Beispiele

Inspiriert von der UCLA einige Beispielstrings:

clear all
input str60 add
"4905 Lakeway Drive, College Station, Texas 77845 USA"
"673 Jasmine Street, Los Angeles, CA 90024"
"2376 First street, San Diego, CA 90126"
"6 West Central St, Tempe AZ 80068"
"1234 Main St. Cambridge, MA 01238-1234"
"Robert-Schuman-Platz 3, 53175 Bonn GERMANY"
"Regensburger Straße 100, 90478 Nürnberg Germany"
"Ammerländer Heerstraße 114-118, 26129 Oldenburg GERMANY"
end
gen d = ustrregexm(add, "GERMANY|Germany")

Das m in ustrregexm steht für match. Mit ustrregexm können wir nach Zeichenketten suchen. Wenn sie gefunden wird, wird eine 1 zurückzugeben, andernfalls 0. Hier suchen wir nach “entweder GERMANY oder Germany”.

Wir können aber auch den gefundenen Inhalt extrahieren. Bei Übereinstimmungen werden die matches gespeichert, darauf können wir mit ustrregexs zurückgreifen:

gen d2 = ustrregexs(0) if ustrregexm(add, "GERMANY|Germany")

s steht für subexpressions (oder “tokens”). Token 0 enthält alle übereinstimmenden Muster. Wird eine Zeichenkette mehrfach gefunden, dann enthält Token 0 alle Übereinstimmungen, Token 1 die erste, Token 2 die zweite und so weiter.

ustrregexrf und ustrregexra helfen uns schließlich, Inhalte zu ersetzen. rf bzw. ra stehen dabei für “replace first” bzw. “replace all”. ustrregexrf ersetzt also nur den ersten match, ustrregexra hingegen alle.

Basierend auf den Regeln von oben ergeben diese beiden Befehle unterschiedliche Ergebnisse:

gen s1 = ustrregexra(add, "street",   "!")
gen s2 = ustrregexra(add, "[street]", "!")

In s1 wurden alle matches “street” gelöscht und durch ! ersetzt. In s2 wurden alle matches von “s”,“t”,“r”,“e” und “t” gelöscht und durch ! ersetzt.

Weitere Beispiele für ersetzen mit regex-Regelausdrücken - siehe DoFile:

gen z1 =  ustrregexra(add, "\w", "") // alle alphanumeric ersetzen
gen z2 = ustrregexra(add, "\W", "") // alle nicht-alphanumeric ersetzen
gen z3 =  ustrregexra(add, "\d", "") // alle Zahlen ersetzen 
gen z4 = ustrregexra(add, "\D", "") // alle nicht-Zahlen ersetzen 
gen z5 = ustrregexra(add, ".+,", "") // alles vor dem Komma ersetzen
gen z6 = ustrregexra(add, ",.+", "") // alles nach dem Komma ersetzen

Nach Zahlen suchen:

gen r1 = ustrregexs(0) if ustrregexm(add, "\d")  // Zahl
gen r2 = ustrregexs(0) if ustrregexm(add, "\d+") // Zahlenfolge
gen r3 = ustrregexs(0) if ustrregexm(add, "(\d{5})") // 5-stellige Zahl
gen r4 = ustrregexs(0) if ustrregexm(add, "^(\d+)") // Zahlenfolge am Anfang
gen r5 = ustrregexs(0) if ustrregexm(add, "(\d+).*(\d+)") // Zahlenfolgen und alles was dazwischen kommt 
gen r6 = ustrregexs(0) if ustrregexm(r5, "(\d+)$") // Zahlenfolge am Ende -> aus r5!

4.3 Label bearbeiten

Um Informationen zu Variablen & Labels abzurufen, stehen eine ganze Reihe an extended macro functions zur Verfügung:

loc v m1202

local vartype:     type `v'                   // Variablen "storage type" (byte etc)
local varlab:      variable label `v' // variable label
local vallabname:  value label `v'    // Name des value label
local vallab1 :    label (`v') 1            // Value label für Wert = 1

Die so erstellten locals können wir dann in der bekannten Methode wieder darstellen:

di "`vartype'"     // display local "vartype"
di "`varlab'"      // display local "varlabel"
di "`vallabname'"  // display local "valuelabname"
di "`vallab1'"     // display local "valuelab1"

Wir können die Labels und Eigenschaften von `v’ auch in einem Schritt anzeigen lassen, die Syntax sieht aber etwas eigenwillig aus:

loc v m1202
di "`: type `v''"                  // "storage type" (byte etc) der Variable
di "`: variable label `v''"  // variable label
di "`: value label `v''"         // Name des value label
di "`: label (`v') 1'"         // Value label für Wert = 1

Damit können wir bspw. ein Variable Label kürzen:

local longlabel: var label m1202        // variable label für variable m1202 suchen
local shortlabel = substr("`longlabel'",1,10) // verändern mit string Funktion 
label var m1202 "`shortlabel'"         // anwenden

Um an die Value Labels zu kommen, braucht es etwas mehr:

local lblname: value label m1202    // value labels für variable m1202 suchen
cap label drop `lblname'_n          //neuen namen droppen zur Sicherheit
label copy `lblname' `lblname'_n    // value labelbook kopieren

local lab1: label (m1202) 2 // value label für Wert = 2 aufrufen
loc lab2 = upper("`lab1'")      // dieses value labels verändern
label define `lblname'_n `lvl' "`lab2'", modify // in neues value labelbook einfügen

labelbook `lblname' `lblname'_n // vergleich alt vs neu

Mit label copy oldname newname, local lblname: value label var und local lab1: label (var) level können wir auch value-Labels bearbeiten:

loc v m1202 

local lblname: value label `v'       // value label aufrufen
cap label drop `lblname'_n           // neuen Namen zur Sicherheit droppen
label copy `lblname' `lblname'_n     // kopieren
levelsof `v', loc(x)                 // Werte für die Variable aufrufen

foreach lvl of local x {
    local lab1: label (`v') `lvl'     // Value label Variable v bei Level lvl
    loc lab2 = substr("`lab1'",1,8)         // kürzen 
    
    label define `lblname'_n `lvl' "`lab2'", modify // im neuen value label ändern
  }
  
lab val `v' `lblname'_n             // anwenden

Übung 2


4.4 Abgleiche

Existiert eine Variable?

capture confirm  variable lm02
if !_rc dis "ja"
if _rc  dis "nein"

Ist variable numerisch?

capture confirm numeric variable az
if !_rc dis "ja"
if _rc  dis "nein"
ja

help data_types

4.5 wo kommt überall -4 vor?

quietly ds
local varlist1 `r(varlist)'
*display "`varlist1'"
foreach v of varlist1 {
  qui count if `v' == -4
  if r(N) > 0 display "`v'"
}

* oder direkt:
foreach v of varlist * {
  qui count if `v' == -4
  if r(N) > 0 display "`v'"
}

Übung


4.6 Übungen

4.6.1 Übung

  • Verwenden Sie mit input die Adressdaten von oben
  • Wie kommen Sie jeweils an das vorletzte Wort aus der Adressliste?
  • Extrahieren Sie die zehn letzten Zeichen aus add, aber lassen die drei letzten Zeichen weg. (“Zeichen 10 bis 4 von hinten her gezählt”).

4.6.2 Übung

  • Laden Sie den regex.dta:
    use "https://github.com/filius23/StataProgBIBB/raw/main/docs/regex1.dta", clear und teilen Sie die Informationen aus address in 4 Variablen auf: Hausnummer (erste Zahl), Straße, PLZ, Region
    • Wandeln Sie alle Einträge in Großbuchstaben um
    • Verwenden Sie split mit geeignetem parse()-Argument, um zwischen Hausnummer & Straße und PLZ & Region zu trennen.
    • Wie können Sie jetzt die Zahlen vom Text trennen? (Tipp: Suchen Sie erst nach den Zahlen. Tipp2: Was wollen wir dann in den Textvariablen nicht mehr?)
    • Löschen Sie ggf. Leerzeichen zu Beginn und am Ende der Variablen

4.6.3 Übung

  • Laden Sie der Erwerbstätigenbefragung

  • Kürzen die die variable labels für alle Variablen mit “wissensintensiver Beruf” im Label (d *wib*)

    • Ersetzen Sie “wissensintensiver Beruf” in den variable labels mit “wib”.
    • Spielen Sie die Routine erst für eine Variable durch: welche Label-Befehle brauchen Sie?
    • Denken Sie an foreach ... of varlist und die Möglichkeit, wildcards zu verwenden. Alternativ hilft evtl. auch ds mit Wildcards

So können Sie überpürfen, ob das geklappt hat:

d *wib*
  • Bearbeiten Sie das value label für nuts2 - nutzen Sie dafür die regex und string-Funktionen von oben
    • Löschen Sie “Statistische” aus den den value labels und ersetzen Sie “Direktionsbezirk” durch “Bezirk”:
tab nuts2
  • Kehren Sie die Codierung vom m1202 um: gen m1202_n = 10 - m1202 und passen Sie die value labels entsprechend an die neue Codierung an.
    • Tipp: auch die value labels müssen dann jeweils 10 - x genommen werden.

Für alle, die schon fertig sind:

  • Wie könnten Sie automatisiert den Variable label für die Muttersprachenvariablen (F1606_*) kürzen, sodass statt “Muttersprache:” nur noch “MSpr” im label steht?

4.6.4 Übung

  • In welchen Variablen aus der Erwerbstätigenbefragung kommt der der Wert -9 vor?
  • Füttern Sie diese Information in mvdecode, um die Missings zu überschreiben.
    • Sammeln Sie die Information, welche Variablen -9 enthalten (Stichwort rekursive macro-Definition)
    • Erstellen Sie einen mvdecode-Befehle, welcher die Information aufnimmt und in allen gefundenen Variablen -9 durch . ersetzt.

4.7 Anhang

Variablen mit bestimmten Eigenschaften identifizieren in ein macro

ds, has(type byte)
loc bytevars `r(varlist)'

foreach v of local bytevars {
    rename `v' b_`v'
}