Für den Umgang mit Dataframes empfielt sich der Einsatz von library(dplyr)
, der in der Unit dplyr dargestellt wird.
Die Darstellungen hier konzentrieren sich auf den Umgang mit Dataframes als spezielles Datenformat. Viele der vorgestellten Techniken sind aber allgemeinerer Art und nicht reserviert für Dataframes. Es gibt gewisse Überschneidungen bzw. Ergänzungen mit der Unit transformation
Obwohl Dataframes in R universeller sind, werden sie häufig so gebraucht, wie Datentabellen in anderen Statistikprogrammen. Hierauf beschränken sich auch die Darstellungen hier.
Dataframes sind zweidimensionale Tabellen mit Spalten, die Namen haben. Spalten von Dataframes können sowohl über ihre Namen, als auch über ihre Nummer angesprochen werden. Im Sinne von R ist ein DataFrame eine Liste mit atomic Vectors gleicher Länge (vgl. data_types ).
Wie alle Datenobjekte in R können auch Dataframes dynamisch generiert, modifiziert, umstrukturiert etc. werden.
# generate data.frame via syntax
data <- data.frame(
nr=c(1,2,3,4,5),
geschl=c('w','m','m','w','w'),
spass=c(10, 8, 7, 9, 3))
# create the same data.frame by typecasting some columns
nr=c(1,2,3,4,5)
geschl <- c('w','m','m','w','w')
spass <- c(10, 8, 7, 9, 3)
data <- data.frame(nr, geschl, spass)
# put an additional column at the end
data['new_column'] <- c(101,102,103,104,105) # name new_column must not exist or will be replaced by the new content
# the attached column must have the same length as the existing columns
# if it is shorter, R begins to cycle, if it is larger, R clips
# show me all the column names
# column number of certain column name may be identified
names(data)
## [1] "nr" "geschl" "spass" "new_column"
# Dataframes can be read from external sources, f. e. local files of the filesystem
# they can come from files of various formats, f.e. spreadsheet data (Excel, etc.) or text.
# first line can specify column names
# columns are converted to arrays/vectors that arm the dataframe.
# dataframes can be read from external sources, f.e. using URLs
# among other commands, read.table() and read.delim() can be used to read data.frames
# they refer to textfiles with tab delimited fields in the rows (lines)
my.data <- read.delim("http://md.psych.bio.uni-goettingen.de/data/virt/v_bmi.txt")
# Löschen aus data.frame
data <-data.frame(
nr=c(1,2,3,4,5),
muell = c(1,1,1,1,1),
geschl=c('w','m','m','w','w'),
spass=c(10, 8, 7, 9, 3))
data # mit Muell Spalte
## nr muell geschl spass
## 1 1 1 w 10
## 2 2 1 m 8
## 3 3 1 m 7
## 4 4 1 w 9
## 5 5 1 w 3
data <- data.frame(nr = data[,1], data[,3:4])
data # jetzt ohne Muell-Spalte
## nr geschl spass
## 1 1 w 10
## 2 2 m 8
## 3 3 m 7
## 4 4 w 9
## 5 5 w 3
# vp 3 löschen
data <- data[data$nr != 3,]
data # data ohne vp 3
## nr geschl spass
## 1 1 w 10
## 2 2 m 8
## 4 4 w 9
## 5 5 w 3
# delete a column in a dataframe by assigning it NULL
data <-data.frame(
subj =c(1,2,3,4,5),
gender=c('f','m','m','f','f'),
fun =c(10, 8, 7, 9, 3))
data
## subj gender fun
## 1 1 f 10
## 2 2 m 8
## 3 3 m 7
## 4 4 f 9
## 5 5 f 3
# now erase column gender
data$gender <- NULL
data
## subj fun
## 1 1 10
## 2 2 8
## 3 3 7
## 4 4 9
## 5 5 3
# invertieren: Columns werden zu rows und umgekehrt
data # original
## subj fun
## 1 1 10
## 2 2 8
## 3 3 7
## 4 4 9
## 5 5 3
t(data) # invertiert
## [,1] [,2] [,3] [,4] [,5]
## subj 1 2 3 4 5
## fun 10 8 7 9 3
# einen Faktor in Abhängigkeit von einer anderen Spalte (Variable) generieren
# einlesen
my.data <- read.delim("http://md.psych.bio.uni-goettingen.de/data/virt/v_bmi_bef.txt")
# spalte gruppe anhängen, default 0
my.data['gruppe'] <- 0
my.data$gruppe[my.data$gew > 100] = 2 # gewicht größer 100: Gruppe = 2
my.data$gruppe[my.data$gew <= 100] = 1 # gewicht kleiner gleich 100: Gruppe = 1
my.data$gruppe <- factor(my.data$gruppe) # und nun zum Faktor deklarieren und die Spalte überschreiben
#? sortieren, umsortieren
# Data Frame in Datei wegschreiben (tab-delimited)
write.table(my.data, "data1.txt", sep="\t")
# wenn die String-Variablen und die Variablennamen in Quotes ("...") stören:
write.table(my.data, "data2.txt", sep="\t", quote=F)
# wenn die Fallnummern stören bzw. das Verrutschen der Variablennamen(Spaltennamen):
write.table(my.data, "data3.txt", sep="\t", quote=F, row.names=F)
# man kann auch eine interaktive Auswahl ermöglichen
# write.table(my.data, file.choose(), sep="\t", quote=F, row.names=F)
# sortieren, umsortieren von data.frame
data <-data.frame(
nr=c(1,2,3,4,5,6),
muell = c(1,2,5,9,10,1),
geschl=c('w','m','m','w','w','m'),
spass=c(10, 8, 7, 9, 3, 9))
# sortieren nach Spalte data$spass
data.sortet <- data[order(data$spass),]
# und zeigen
data
## nr muell geschl spass
## 1 1 1 w 10
## 2 2 2 m 8
## 3 3 5 m 7
## 4 4 9 w 9
## 5 5 10 w 3
## 6 6 1 m 9
data.sortet
## nr muell geschl spass
## 5 5 10 w 3
## 3 3 5 m 7
## 2 2 2 m 8
## 4 4 9 w 9
## 6 6 1 m 9
## 1 1 1 w 10
# sortieren nach zwei Spalten: data$spass und sekundär nach data$muell
data.sortet <- data[order(data$spass, data$muell),]
# und zeigen
data.sortet
## nr muell geschl spass
## 5 5 10 w 3
## 3 3 5 m 7
## 2 2 2 m 8
## 6 6 1 m 9
## 4 4 9 w 9
## 1 1 1 w 10
Zugriff auf Spalten/Variablen in einem Dataframe erhält man über die Notation Dataframe$Spaltenname. Oft bewegt man sich, zumindest eine temporär, nur in den Variablen eines Dataframe. Dann ist es lästig, immer den Dataframe mit angeben zu müssen. Mit attach(Dataframe)
kann man die Spaltennamen eines Dataframe vorübergehend in den globalen Namensraum (global namespace) übernehmen. Mit detach(Dataframe)
wird das wieder rückgängig gemacht. Wenn es im globalen Namensraum (global namespace) objekte gleichen Namens gibt, kommt es u. U. zu unvorhergesehenen Effekten (Überschreibungen bzw. Ausmaskierungen). Hier kann man explizit einen Namen eines speziellen Namespace mit ::
vorn anhängen. Beispiel: Rufe describe()
aus dem Paket psych
auf: psych::describe()
# read a dataframe
bmi <- read.delim("http://md.psych.bio.uni-goettingen.de/data/virt/v_bmi.txt")
# get column weight of dataframe bmi
bmi$weight
## [1] 60 54 110 52 62 41 71 70 44 60 45 130 91 86 56 47 52
## [18] 53 90 80 90 100 60 75 80 54 58 57 53 54
# calculate body mass index using full names
bmi$weight / (bmi$height / 100) ** 2
## [1] 18.51852 17.43285 33.95062 15.69859 24.21875 16.84747 26.07897
## [8] 27.34375 17.40437 16.79684 18.02596 35.26476 26.02305 23.08787
## [15] 18.71095 17.26354 18.64534 17.30612 27.77778 25.53545 28.08901
## [22] 30.18959 18.93700 22.89307 23.37473 16.66667 17.13138 17.78971
## [29] 15.82609 20.57613
# now attach columns of bmi to global namespace
attach(bmi)
print('dataframe bmi attached')
## [1] "dataframe bmi attached"
# get column weight of dataframe bmi
weight
## [1] 60 54 110 52 62 41 71 70 44 60 45 130 91 86 56 47 52
## [18] 53 90 80 90 100 60 75 80 54 58 57 53 54
# calculate body mass index using atached column names
weight / (height / 100) ** 2
## [1] 18.51852 17.43285 33.95062 15.69859 24.21875 16.84747 26.07897
## [8] 27.34375 17.40437 16.79684 18.02596 35.26476 26.02305 23.08787
## [15] 18.71095 17.26354 18.64534 17.30612 27.77778 25.53545 28.08901
## [22] 30.18959 18.93700 22.89307 23.37473 16.66667 17.13138 17.78971
## [29] 15.82609 20.57613
# detach dataframe bmi
detach(bmi)
# name 'weight' is not accessible any more
print('dataframe bmi detached')
## [1] "dataframe bmi detached"
# weight # would cause error
Slicing: Zugriff auf Teile von Datenobjekten
Spalten von Dataframes sind Vektoren. Slicing bei Vektoren ist dargestellt in der unit transformation_base und gilt analog für Dataframes.
Zugriff auf Spalten kann auf verschiedene Arten erfolgen.
$
Operator und Spaltennamen.# create sample dataframe
data <-data.frame(
subj =c(1,2,3,4,5),
gender=c('f','m','m','f','f'),
fun =c(10, 8, 7, 9, 3))
# access using $ operator
data$gender
## [1] f m m f f
## Levels: f m
# one argument slice refers to columns
# get gender column
data[2]
## gender
## 1 f
## 2 m
## 3 m
## 4 f
## 5 f
# get gender and fun
data[c(2,3)]
## gender fun
## 1 f 10
## 2 m 8
## 3 m 7
## 4 f 9
## 5 f 3
# get gender column by name
data['gender']
## gender
## 1 f
## 2 m
## 3 m
## 4 f
## 5 f
# get gender and fun by name
data[c('fun', 'gender')]
## fun gender
## 1 10 f
## 2 8 m
## 3 7 m
## 4 9 f
## 5 3 f
# using two parameters, the first refers to rows, the second to columns
# default is, to get all elements
# the followin calls all return the complete dataframe
data
## subj gender fun
## 1 1 f 10
## 2 2 m 8
## 3 3 m 7
## 4 4 f 9
## 5 5 f 3
data[]
## subj gender fun
## 1 1 f 10
## 2 2 m 8
## 3 3 m 7
## 4 4 f 9
## 5 5 f 3
data[,]
## subj gender fun
## 1 1 f 10
## 2 2 m 8
## 3 3 m 7
## 4 4 f 9
## 5 5 f 3
data[,c(1, 2, 3)]
## subj gender fun
## 1 1 f 10
## 2 2 m 8
## 3 3 m 7
## 4 4 f 9
## 5 5 f 3
data[,1:3]
## subj gender fun
## 1 1 f 10
## 2 2 m 8
## 3 3 m 7
## 4 4 f 9
## 5 5 f 3
data[,c('subj', 'gender', 'fun')]
## subj gender fun
## 1 1 f 10
## 2 2 m 8
## 3 3 m 7
## 4 4 f 9
## 5 5 f 3
# get gender using 'two' parameters
data[,2]
## [1] f m m f f
## Levels: f m
data[,'gender']
## [1] f m m f f
## Levels: f m
Bei zwei Parametern in der Slice-Klammer, durch Kommata getrennt, bezieht sich der erste auf Zeilen, der zweite auf Spalten ‘[Zeilen, Spalten]’. Fehlt eine der beiden Angaben, werden als Voreinstellung alle Elemente zurückgegeben. In Slices können auch Bedingungen formuliert werden.
data <-data.frame(
nr=c(1,2,3,4,5),
geschl=c('w','m','m','w','w'),
spass=c(10, 8, 7, 9, 3))
# erste Zeile (Vp 1) zweite Variable (Geschlecht) ausgeben:
data[1,2]
## [1] w
## Levels: m w
# erste Zeile/Vp ausgeben, alle Variablen
data[1,]
## nr geschl spass
## 1 1 w 10
# das geht auch mit dem bedingten Zugriff
# alle Frauen
data[data$geschl == 'w',]
## nr geschl spass
## 1 1 w 10
## 4 4 w 9
## 5 5 w 3
# alle Frauen mit Spass < 10
data[data$geschl == 'w' & data$spass < 10,]
## nr geschl spass
## 4 4 w 9
## 5 5 w 3
Die Klassen aller Spalten eines data.frame
dfbmi <- read.delim("http://md.psych.bio.uni-goettingen.de/data/virt/v_bmi.txt")
str(dfbmi)
## 'data.frame': 30 obs. of 12 variables:
## $ subj : int 1 2 3 4 5 6 7 8 9 10 ...
## $ name : Factor w/ 30 levels "Abdullah","Ärger",..: 18 22 26 28 2 23 30 17 15 16 ...
## $ gender : int 2 2 1 1 1 1 1 1 1 2 ...
## $ height : int 180 176 180 182 160 156 165 160 159 189 ...
## $ weight : int 60 54 110 52 62 41 71 70 44 60 ...
## $ grade : num 2.02 1 2.03 1 1 1 2.47 2.37 2.26 1.21 ...
## $ c_phys_app : int 2 3 5 4 3 3 4 4 4 5 ...
## $ c_good_way : int 1 4 5 5 2 4 3 4 3 4 ...
## $ c_dress : int 2 3 5 4 2 3 4 4 3 5 ...
## $ c_bad_way : int 4 1 1 2 4 3 2 2 2 1 ...
## $ c_figure : int 1 5 5 3 1 4 3 4 4 4 ...
## $ filling_time: num 39.6 26 26 26 26 ...
lapply(dfbmi, class)
## $subj
## [1] "integer"
##
## $name
## [1] "factor"
##
## $gender
## [1] "integer"
##
## $height
## [1] "integer"
##
## $weight
## [1] "integer"
##
## $grade
## [1] "numeric"
##
## $c_phys_app
## [1] "integer"
##
## $c_good_way
## [1] "integer"
##
## $c_dress
## [1] "integer"
##
## $c_bad_way
## [1] "integer"
##
## $c_figure
## [1] "integer"
##
## $filling_time
## [1] "numeric"
which()
# create sample dataframe
ddf <-data.frame(
subj =c(1,2,3,4,5),
age =c(22, 23, 27, 19, 26),
gender =c('f','m','m','f','f'),
fun1 =c(10, 8, 7, 9, 3),
fun2 =c(13, 9, 8, 9, 5),
fun3 =c( 9, 4, 7, 4, 5))
# based on variable values, care for comma before closing bracket "[..., ]"
ddf[ which(ddf$gender=='f'& ddf$age > 25), ]
## subj age gender fun1 fun2 fun3
## 5 5 26 f 3 5 5
# or with attached dataframe
attach(ddf)
ddf[ which(gender=='f' & age > 25),]
## subj age gender fun1 fun2 fun3
## 5 5 26 f 3 5 5
detach(ddf)
grep ermöglicht suchen in Text nach regulären Ausdrücken.
ddf <- read.delim("http://md.psych.bio.uni-goettingen.de/data/virt/v_bmi.txt")
# column names
names(ddf)
## [1] "subj" "name" "gender" "height"
## [5] "weight" "grade" "c_phys_app" "c_good_way"
## [9] "c_dress" "c_bad_way" "c_figure" "filling_time"
# get all pacs items by number
ddf[1:5,c(7, 8, 9, 10, 11)]
## c_phys_app c_good_way c_dress c_bad_way c_figure
## 1 2 1 2 4 1
## 2 3 4 3 1 5
## 3 5 5 5 1 5
## 4 4 5 4 2 3
## 5 3 2 2 4 1
# use : operator
ddf[1:5,c(7:11)]
## c_phys_app c_good_way c_dress c_bad_way c_figure
## 1 2 1 2 4 1
## 2 3 4 3 1 5
## 3 5 5 5 1 5
## 4 4 5 4 2 3
## 5 3 2 2 4 1
# use array of column names explicitly
ddf[1:5, c("c_phys_app", "c_good_way", "c_dress", "c_bad_way", "c_figure")]
## c_phys_app c_good_way c_dress c_bad_way c_figure
## 1 2 1 2 4 1
## 2 3 4 3 1 5
## 3 5 5 5 1 5
## 4 4 5 4 2 3
## 5 3 2 2 4 1
# or by defining name arry before selection
pacs_items = c("c_phys_app", "c_good_way", "c_dress", "c_bad_way", "c_figure")
ddf[1:5, pacs_items]
## c_phys_app c_good_way c_dress c_bad_way c_figure
## 1 2 1 2 4 1
## 2 3 4 3 1 5
## 3 5 5 5 1 5
## 4 4 5 4 2 3
## 5 3 2 2 4 1
# dynamic selection by using grep()
# all pacs items begin with c_
pacs_items <- grep('c_', names(ddf))
head(ddf[pacs_items])
## c_phys_app c_good_way c_dress c_bad_way c_figure
## 1 2 1 2 4 1
## 2 3 4 3 1 5
## 3 5 5 5 1 5
## 4 4 5 4 2 3
## 5 3 2 2 4 1
## 6 3 4 3 3 4
Subset ermöglicht sehr flexiblen Zugriff auf Teil-Datasets. Leider kann man Subsets nicht benutzen, um Zuweisungen in DataFrames zu machen. Daher ist der Befehl subset()
nicht gut geeignet, um Transformationen auszuführen.
Quick-R Seite zu subset
apply()
Hier nochmals der Verweis auf das Package dplyr()
, das sehr viel intuitivere Befehle zur Datenmanipulation zur Verfügung stellt. vgl Unit dplyr
Befehl apply()
Parameter sind - Objekt (Matrix, Teil-Datenframe), auf dessen Elemente eine Funktion angewendet wird - Richtung: 1: zeilenweise, 2: spaltenweise - Funktion: wird angewendet
apply()
ist geeignet, um zeilenweise bestimmte Aktionen auszuführen. Dies entspricht dem Anwendungsfall, Variablen innerhalb von Beobachtungen zu aggregieren. Beispiel: Einzelitems zu Skalenwerten zusammenzufassen - für jede einzelne Beobachtung.
# create sample dataframe
ddf <-data.frame(
subj =c(1,2,3,4,5),
gender=c('f','m','m','f','f'),
fun1 =c(10, 8, 7, 9, 3),
fun2 =c(13, 9, 8, 9, 5),
fun3 =c( 9, 4, 7, 4, 5))
ddf
## subj gender fun1 fun2 fun3
## 1 1 f 10 13 9
## 2 2 m 8 9 4
## 3 3 m 7 8 7
## 4 4 f 9 9 4
## 5 5 f 3 5 5
# get min, max of the fun* items for each observation (subj)
apply(ddf[c('fun1', 'fun2', 'fun3')], 1, min)
## [1] 9 4 7 4 3
apply(ddf[c('fun1', 'fun2', 'fun3')], 1, max)
## [1] 13 9 8 9 5
# get mean of fun1 and fun2 for each observation (subj)
apply(ddf[c('fun1', 'fun2', 'fun3')], 1, mean)
## [1] 10.666667 7.000000 7.333333 7.333333 4.333333
# this also works vertically, by coumns
# get mean of the whole column of fun1 and fun2
apply(ddf[c('fun1', 'fun2')], 2, mean)
## fun1 fun2
## 7.4 8.8
# store result in newly generated column in dataframe
# it contains mean of fun1 and fun2 for each subject
ddf$mean_fun <- apply(ddf[c('fun1', 'fun2')], 1, mean)
ddf
## subj gender fun1 fun2 fun3 mean_fun
## 1 1 f 10 13 9 11.5
## 2 2 m 8 9 4 8.5
## 3 3 m 7 8 7 7.5
## 4 4 f 9 9 4 9.0
## 5 5 f 3 5 5 4.0
apply()
bei DataframesEigene Funktionen können einfach definiert werden: `function(parameters){commands}
Im Befehl apply()
können Funktionen ‘on the fly’ definiert werden.
ddf <-data.frame(
subj =c(1,2,3,4,5),
gender=c('f','m','m','f','f'),
fun1 =c(10, 8, 7, 9, 3),
fun2 =c(13, 9, 8, 9, 5),
fun3 =c( 9, 20, 7, 4, 5))
# get each value squared and divided by 2
apply(ddf[c('fun1', 'fun2', 'fun3')], 1, function(x) (x^2)/2)
## [,1] [,2] [,3] [,4] [,5]
## fun1 50.0 32.0 24.5 40.5 4.5
## fun2 84.5 40.5 32.0 40.5 12.5
## fun3 40.5 200.0 24.5 8.0 12.5
Aber auch vorher definierte Funktionen können eingesetzt werden. Diese Funktionen erhalten als ersten Parameter den ausgewählten Slice von apply, weitere können folgen. Hier ein Beispiel, in dem für jede untersuchte Person geschaut werden soll, ob bestimmte, kritische Werte in einer Gruppe von Variablen, z. B. Items, vorkommen.
# create sample dataframe
ddf <-data.frame(
subj =c(1,2,3,4,5),
gender=c('f','m','m','f','f'),
fun1 =c(10, 8, 7, 9, 3),
fun2 =c(13, 9, 8, 9, 5),
fun3 =c( 9, 20, 7, 4, 5))
ddf
## subj gender fun1 fun2 fun3
## 1 1 f 10 13 9
## 2 2 m 8 9 20
## 3 3 m 7 8 7
## 4 4 f 9 9 4
## 5 5 f 3 5 5
# function that tests, whether one of some values is contained in vector of values (c.f. transformations)
test.for.one.of <- function(x, criticals){
sum(is.element(criticals, x)) > 0
}
# find out, which subj has any of the critical values in any of his fun* items
apply(ddf[c('fun1', 'fun2', 'fun3')], 1, test.for.one.of, criticals = c(10, 20))
## [1] TRUE TRUE FALSE FALSE FALSE
# get mean of fun1 and fun2 for each observation (subj)
apply(ddf[c('fun1', 'fun2')], 1, mean)
## [1] 11.5 8.5 7.5 9.0 4.0
# get mean of the whole column of fun1 and fun2
apply(ddf[c('fun1', 'fun2')], 2, mean)
## fun1 fun2
## 7.4 8.8
# store result in newly generated column in dataframe
# it contains mean of fun1 and fun2 for each subject
ddf$mean_fun <- apply(ddf[c('fun1', 'fun2')], 1, mean)
ddf
## subj gender fun1 fun2 fun3 mean_fun
## 1 1 f 10 13 9 11.5
## 2 2 m 8 9 20 8.5
## 3 3 m 7 8 7 7.5
## 4 4 f 9 9 4 9.0
## 5 5 f 3 5 5 4.0
Behandlung mit dplyr two table verbs
Entsprechende Seite in Quick-R
Zwei Datenframes mit derselben Struktur (denselben Spaltennamen) können mit dem Befehl rbind()
aneinandergehängt werden. Dies entspricht in etwa dem merge
Befehl in anderen Paketen. rbind()
bezieht sich auf Zeilen, wie sich cbind()
auf Spalten bezieht. Mit rbind()
werden Zeilen angehängt. Dies entspricht dem Hinzufügen von Beobachtungen (Personen).
rbind verknüpft nur Dataframes mit den gleichen Spalten (gleiche Spaltennamen).
Entweder, die Variable(n), die es in einem Teil-Dataframe nicht gibt, werden dort gelöscht, wo sie existieren, oder die fehlenden Variablen werden dort, wo sie fehlen, generiert und sinnvollerweise mit NA vorbesetzt. Dies muss vor dem rbind()
Befehl passieren.
# data of s1 and s2 that have the same variables, i.e. add more observations with the same variables
data.s1 <- data.frame(
id = c( 1, 2, 3),
gender = c('w', 'm', 'w'),
age = c( 20, 22, 27)
)
# variable sequence in dataframe doesn't matter
data.s2 <- data.frame(
gender = c('w', 'm'),
id = c( 4, 5),
age = c( 19, 23)
)
data.all <- rbind(data.s1, data.s2)
data.all
## id gender age
## 1 1 w 20
## 2 2 m 22
## 3 3 w 27
## 4 4 w 19
## 5 5 m 23
# if a variable is missing in a dataframe, create it and fill it with NA (missings)
# in data.s3 age is missing
data.s3 <- data.frame(
gender = c('m', 'w'),
id = c( 6, 7)
)
# rbinding it directly would cause an error
# rbind(data.all, data.t3)
# Fehler in rbind(deparse.level, ...) :
# Anzahl der Spalten der Argumente unterschiedlich
# so we aggregate the missing column
data.s3$age = NA
rbind(data.all, data.s3)
## id gender age
## 1 1 w 20
## 2 2 m 22
## 3 3 w 27
## 4 4 w 19
## 5 5 m 23
## 6 6 m NA
## 7 7 w NA
# or
rbind(data.s1, data.s2, data.s3)
## id gender age
## 1 1 w 20
## 2 2 m 22
## 3 3 w 27
## 4 4 w 19
## 5 5 m 23
## 6 6 m NA
## 7 7 w NA
# the same can be done using merge, usually used to add columns/variables
# but take care to specify all column names in the 'by=c()' part and add the all=T flag
merge(data.s1, data.s2, by=c('id','gender', 'age'), all=T)
## id gender age
## 1 1 w 20
## 2 2 m 22
## 3 3 w 27
## 4 4 w 19
## 5 5 m 23
# also with merge(), both dataframes must have all variables/columns, sequence doesn't matter
Bei regelmäßigen und vollständigen Teil-Datenframes gibt es keine Probleme
# data of v1: three subjects, 3 vars
ddf.v1 <- data.frame(
subj = c( 1, 2, 3),
gender = c('w', 'm', 'w'),
age = c( 20, 22, 27)
)
# data of v2, same subjects, two more vars
ddf.v2 <- data.frame(
subj = c( 1, 2, 3),
weight = c( 67, 85, 78),
height = c(172, 185, 180)
)
# if there are no missings and no aditional subjects it's easy and can be done using cbind()
cbind(ddf.v1, ddf.v2) # this duplicates subj column, what doesnt make sense
## subj gender age subj weight height
## 1 1 w 20 1 67 172
## 2 2 m 22 2 85 185
## 3 3 w 27 3 78 180
# without redundancy
cbind(ddf.v1, ddf.v2[c('weight', 'height')])
## subj gender age weight height
## 1 1 w 20 67 172
## 2 2 m 22 85 185
## 3 3 w 27 78 180
# the same using merge
# argument `by` specifies comon columns in the two dataframes
merge(ddf.v1, ddf.v2, by="subj")
## subj gender age weight height
## 1 1 w 20 67 172
## 2 2 m 22 85 185
## 3 3 w 27 78 180
Ein Dataframe enthält mehr Beobachtungen als der andere, keine Überschneidung bei den Variablen
# data of v1: three subjects, 3 vars
ddf.v1 <- data.frame(
subj = c( 1, 2, 3),
gender = c('w', 'm', 'w'),
age = c( 20, 22, 27)
)
# data of v2, more subjects, two more vars
ddf.v2 <- data.frame(
subj = c( 1, 2, 3, 4, 5),
weight = c( 67, 85, 78, 66, 72),
height = c(172, 185, 180, 165, 177)
)
# problem: what to do with subjects, that only exist in one dataframe
# solution 1: get only complete subjects after merge
merge(ddf.v1, ddf.v2, by="subj")
## subj gender age weight height
## 1 1 w 20 67 172
## 2 2 m 22 85 185
## 3 3 w 27 78 180
# solution 2: impute NA where data are missing by using flag `all`
merge(ddf.v1, ddf.v2, by="subj", all=T)
## subj gender age weight height
## 1 1 w 20 67 172
## 2 2 m 22 85 185
## 3 3 w 27 78 180
## 4 4 <NA> NA 66 165
## 5 5 <NA> NA 72 177
Ein Dataframe enthält mehr Beobachtungen als der andere, Überschneidung bei den Variablen, Inkonsistenzen bei zusammengehörigen Variablen.
# data of v1: three subjects, 3 vars
ddf.v1 <- data.frame(
subj = c( 1, 2, 3),
gender = c('w', 'm', 'w'),
age = c( 20, 22, 27)
)
# data of v2, more subjects, two more vars
ddf.v2 <- data.frame(
subj = c( 1, 2, 3, 4, 5),
weight = c( 67, 85, 78, 66, 72),
gender = c('w', 'm', 'w', 'w', 'm'),
height = c(172, 185, 180, 165, 177)
)
# take care: gender of ddf.v2 replaces gender of ddf.v1
# variables, the dataframes have in common, have to appear in parameter `by`
# there still are missing data, so the problem of what to do with incomplete data persists
# solution 1: get only complete subjects after merge
merge(ddf.v1, ddf.v2, by=c("subj", "gender"))
## subj gender age weight height
## 1 1 w 20 67 172
## 2 2 m 22 85 185
## 3 3 w 27 78 180
# solution 2: impute NA where data are missing by using flag `all`
merge(ddf.v1, ddf.v2, by=c("subj", "gender"), all=T)
## subj gender age weight height
## 1 1 w 20 67 172
## 2 2 m 22 85 185
## 3 3 w 27 78 180
## 4 4 w NA 66 165
## 5 5 m NA 72 177
# take care: variables in common have to be equal in both dataframes for all subjects
# example: gender of subj 2 is different in the two dataframes
# data of v1: three subjects, 3 vars
ddf.v1 <- data.frame(
subj = c( 1, 2, 3),
gender = c('w', 'm', 'w'),
age = c( 20, 22, 27)
)
# data of v2, more subjects, two more vars, subj 2 has a typo in variable gender
ddf.v2 <- data.frame(
subj = c( 1, 2, 3, 4, 5),
weight = c( 67, 85, 78, 66, 72),
gender = c('w', 'x', 'w', 'w', 'm'),
height = c(172, 185, 180, 165, 177)
)
# take care: gender of ddf.v2 replaces gender of ddf.v1
# variables, the dataframes have in common, have to appear in parameter `by`
# there still are missing data, so the problem of what to do with incomplete data persists
# solution 1: get only complete subjects after merge
merge(ddf.v1, ddf.v2, by=c("subj", "gender"))
## subj gender age weight height
## 1 1 w 20 67 172
## 2 3 w 27 78 180
# solution 2: impute NA where data are missing by using flag `all`
merge(ddf.v1, ddf.v2, by=c("subj", "gender"), all=T)
## subj gender age weight height
## 1 1 w 20 67 172
## 2 2 m 22 NA NA
## 3 2 x NA 85 185
## 4 3 w 27 78 180
## 5 4 w NA 66 165
## 6 5 m NA 72 177
Ein
Tutorial von Christian Treffenstädt zum Datenzugriff in R und zu Dataframes.