Archiv der Kategorie: Funktion

Erweiterte Tipps und Tricks mit data.table

1. DATENSTRUKTUREN & ZUORDNUNG

Spalten von Listen
Zugriff auf Elemente aus einer Spalte von Listen heraus
Unterdrücken der Zwischenausgabe mit {}
Schnelles Looping mit Set. Mit dem sverweis mehrere spalten ausgeben und auswerten.
Verwendung der Verschiebung zum Führen/Lagern von Vektoren und Listen
Erstellen Sie mehrere Spalten mit := in einer Anweisung.
Weisen Sie eine Spalte mit := benannt mit einem Zeichenobjekt zu.

2. BY

Berechnen einer Funktion über eine Gruppe (mit by), wobei jede Entität in einer zweiten Kategorie ausgeschlossen wird.
METHODE 1: In-line
METHODE 2: Verwendung von {} und .SD
METHODE 3: Superschnelle Mittelwertberechnung
Geschwindigkeitskontrolle
Schlüssel für Schlüssel, die sich daraus ergebende aggregierte Tabelle
Verwenden von [1], [.N], setkey und by für innerhalb der Gruppenunterordnung

3. FUNKTIONEN

Übergabe von Spaltennamen der data.table als Funktionsargumente
Methode 2: Angebote und erhalten Sie
Vorsicht vor dem Scoping in der data.table
data.frame way
data.table Weg

Excel Listen

4. DRUCKEN

Drucke data.table mit [].
Ausgabe ausblenden von := mit knitritritr
Unterwegs erlernte Tipps und Tricks
Dies ist meist eine laufende Liste von data.table-Tricks, die mich eine Weile in Anspruch genommen hat, um herauszufinden, entweder durch das Graben in der offiziellen Dokumentation, die Anpassung von StackOverflow-Posts oder häufiger als nicht, stundenlanges Experimentieren. Ich möchte diese Entdeckungen irgendwo mit mehr Speicher als mein Kopf (Hallo Internet) fortsetzen, damit ich sie wiederverwenden kann, nachdem mein mentales Gedächtnis sie vergessen hat. Eine weniger organisierte und prägnante Ergänzung zum süßen Spickzettel von DataCamp für die Grundlagen.
Die meisten, wenn nicht sogar alle diese Techniken wurden für echte Data-Science-Projekte entwickelt und boten einen gewissen Mehrwert für mein Data Engineering. Ich habe alles auf den mtcars-Datensatz verallgemeinert, was diesen Wert in diesem leicht konstruierten Kontext möglicherweise nicht sofort deutlich macht. Diese Liste ist nicht vollständig, wie das DataCamp data.table Cheatsheet. OK, genug Haftungsausschlüsse!
Einige erweiterte Funktionen von data.table Creator Matt Dowle hier.

1. DATENSTRUKTUREN & ZUORDNUNG

Spalten von Listen
Übersichtstabelle (lang und schmal)
Dies könnte nützlich sein, ist aber mit traditionellen Methoden leicht zu erreichen.
dt <- data.table(mtcars)[, .(cyl, gear)]]
dt[,unique(gear), by=cyl]
## Zylinder V1
## 1: 6 4
## 2: 6 3
## 3: 6 5
## 4: 4 4
## 5: 4 3
## 6: 4 5
## 7: 8 3
## 8: 8 5
Übersichtstabelle (kurz und bündig)
Fügen Sie alle Kategorien von Zahnrädern für jeden Zylinder der Original data.table als Liste hinzu.

Das ist raffinierter. Es ist so einfach, dass ich diesen Trick benutze, um Daten schnell ad hoc auf der Kommandozeile zu erkunden. Kann auch für anspruchsvollere Datentechnik nützlich sein.
dt <- data.table(mtcars)[,.(gear, cyl)]]
dt[,gearsL:=list(list(unique(gear))), by=cyl] # original, hässlich
dt[,gearsL:=.(list(unique(gear))), by=cyl] # verbessert, schön
Kopf(dt)
## Zahnrad-ZylinderräderL
## 1: 4 6 4,3,5
## 2: 4 6 4,3,5
## 3: 4 4 4,3,5
## 4: 3 6 4,3,5
## 5: 3 8 3,5
## 6: 3 6 4,3,5
Aktualisierung 29.10.2015: Gemäß diesen Kommentaren zu StackOverlow, die sich auf meinen Beitrag beziehen, kann t[,gearsL:=list(list(unique(gear))), by=cyl] eleganter als t[,gearsL:=.(list(unique(gear))), by=cyl] geschrieben werden. Danke, dass du auf meine unnötig wortreiche und ungewöhnliche Syntax hingewiesen hast! Ich denke, ich habe das erste geschrieben, was funktionierte, als ich dies gepostet habe, ohne zu erkennen, dass die normale Syntax der äußeren Liste entsprach.
Zugriff auf Elemente aus einer Spalte von Listen heraus
Extrahieren Sie das zweite Element jeder Liste in gearL1 und erstellen Sie die Zeile gearL1. Dies ist nicht gerade bahnbrechend, sondern untersucht, wie man auf Elemente von Spalten zugreift, die aus Listen von Listen aufgebaut sind. lapply ist Ihr Freund.
dt[,gearL1:=lapply(gearsL, function(x) x[2]]]
dt[,gearS1:=sapply(gearsL, function(x) x[2]]]

Kopf(dt)
## Zahnrad-ZylinderräderL ZahnradL1 ZahnräderL1 ZahnräderS1
## 1: 4 6 4,3,5 3 3
## 2: 4 6 4,3,5 3 3
## 3: 4 4 4,3,5 3 3
## 4: 3 6 4,3,5 3 3
## 5: 3 8 3,5 5 5
## 6: 3 6 4,3,5 3 3
str(head(dt[,gearL1])))
## Liste der 6
## $ : nummer 3
## $ : nummer 3
## $ : nummer 3
## $ : nummer 3
## $ : nummer 5
## $ : nummer 3
str(head(dt[,gearS1])))
## num [1:6] 3 3 3 3 3 3 3 3 5 3 3
Aktualisierung 24.9.2015: Per Matt Dowle’s Kommentare, eine etwas syntaktisch prägnantere Art, dies zu tun:
dt[,gearL1:=lapply(gearsL, `[`, 2)]
dt[,gearS1:=sapply(gearsL, `[`, 2)]
Berechnen Sie alle Gänge für alle Fahrzeuge eines jeden Zylinders (mit Ausnahme der aktuellen Zeile). Dies kann nützlich sein, um Beobachtungen mit dem Mittelwert von Gruppen zu vergleichen, bei denen der Mittelwert der Gruppe nicht durch die Beobachtung von Interesse beeinflusst wird.
dt[,other_gear:=mapply(function(x, y) setdiff(x, y), x=gearsL, y=gear)]]]
Kopf(dt)
## Zahnrad-ZylinderräderL ZahnradL1 ZahnradL1 ZahnradS1 anderes_Getriebe
## 1: 4 6 4,3,5 3 3 3,5
## 2: 4 6 4,3,5 3 3 3,5
## 3: 4 4 4,3,5 3 3 3,5
## 4: 3 6 4,3,5 3 3 4,5
## 5: 3 8 3,5 5 5 5
## 6: 3 6 4,3,5 3 3 4,5
Aktualisierung 24.9.2015: Per Matt Dowle’s Kommentare, dies erreicht das gleiche wie oben.
dt[,other_gear:=mapply(setdiff, gearsL, gear)]]
Unterdrücken der Zwischenausgabe mit {}

Dies ist eigentlich ein Basis-R-Trick, den ich erst bei der Arbeit mit data.table entdeckt habe. Siehe ?`{` für einige Dokumentationen und Beispiele. Ich habe es nur innerhalb des J-Slots von data.table verwendet, es könnte allgemeiner sein. Ich finde es ziemlich nützlich, um Spalten spontan zu erzeugen, wenn ich eine mehrstufige vektorisierte Operation durchführen muss. Es kann Code bereinigen, indem es Ihnen erlaubt, die gleiche temporäre Variable durch einen prägnanten Namen zu referenzieren, anstatt den Code neu zu schreiben, um ihn neu zu berechnen.
dt <- data.table(mtcars)
Standardmäßig wird nur das letzte Objekt zurückgegeben, das in den Klammern unbenannt definiert ist.
dt[,{tmp1=mittel(mpg); tmp2=mittel(abs(mpg-tmp1))); tmp3=rund(tmp2, 2)}, by=cyl]
## Zylinder V1
## 1: 6 1.19
## 2: 4 3.83
## 3: 8 1.79
Wir können expliziter sein, indem wir eine benannte Liste dessen übergeben, was wir behalten wollen.
dt[,{tmp1=mittel(mpg); tmp2=mittel(abs(mpg-tmp1)); tmp3=rund(tmp2, 2); list(tmp2=tmp2, tmp3=tmp3)}, by=cyl]
## cyl tmp2 tmp2 tmp3 tmp3
## 1: 6 1.191837 1.19
## 2: 4 3.833058 3.83
## 3: 8 1.785714 1.79
Kann es auch so ohne Semikolon schreiben.
dt[,{tmp1=mittel(mpg)
tmp2=mittel(abs(mpg-tmp1)))
tmp3=rund(tmp2, 2)
list(tmp2=tmp2, tmp3=tmp3)},
by=cyl]
## cyl tmp2 tmp2 tmp3 tmp3
## 1: 6 1.191837 1.19
## 2: 4 3.833058 3.83
## 3: 8 1.785714 1.79

Das ist kniffliger mit := Zuweisungen… Ich glaube nicht, dass := dazu bestimmt ist, zu funktionieren, wenn es in { eingepackt ist. Wenn Sie mehrere Spalten mit := auf einmal zuweisen, können Sie die ersten Spalten, die Sie erstellen, nicht verwenden, um die folgenden Spalten zu erstellen, wie wir es mit = innerhalb der { oben getan haben. Das Verketten und dann das Löschen unerwünschter Variablen ist ein unordentlicher Workaround…. immer noch das Erkunden dieser Variablen.
dt <- data.table(mtcars)[,.(cyl, mpg)]]

dt[,tmp1:=mittel(mpg), by=cyl][,tmp2:=mittel(abs(mpg-tmp1)), by=cyl][,tmp1:=NULL]
Kopf(dt)
## cyl mpg tmp2
## 1: 6 21.0 1.191837
## 2: 6 21.0 1.191837
## 3: 4 22.8 3.833058
## 4: 6 21.4 1.191837
## 5: 8 18.7 1.785714
## 6: 6 18.1 1.191837
Schnelles Looping mit Set
Ich habe immer noch nicht viel mit dem Loop + Set Framework gearbeitet. Ich konnte mit := so ziemlich alles erreichen, was flexibler und leistungsfähiger ist. Wenn Sie jedoch eine Schleife benötigen, ist das Setzen um Größenordnungen schneller als native R-Zuweisungen innerhalb von Schleifen. Hier ist ein Ausschnitt aus den Neuigkeiten von data.table vor einiger Zeit:
Neuer Funktionssatz (DT,i,j,value) ermöglicht schnelle Zuordnung zu Elementen
von DT. Ähnlich wie :=, vermeidet aber den Overhead von [.data.table, so ist es auch hier.
viel schneller in einer Schleife. Weniger flexibel als :=, aber so flexibel.
als Matrix-Unterkontierung. Ähnlich wie bei setnames(), setcolorder(),
setkey() und setattr(), d.h., weist per Referenz ohne jegliche Kopie zu.

  • M = Matrix(1,nrow=100000,ncol=100)
  • DF = als.Daten.Rahmen(M)
  • DT = als.data.table(M)
  • system.time(for (i in 1:1000) DF[i,1L] <- i) # 591.000s
  • system.time(for (i in 1:1000) DT[i,V1:=i]) # 1.158s
  • system.time(for (i in 1:1000) M[i,1L] <- i) # 0.016s
  • system.time(for (i in 1:1000) set(DT,i,1L,i))) # 0.027s
  • data.table Creators bevorzugen set für einige Dinge, wie diese Aufgabe, die auch mit
  • lapply und .SD erledigt werden kann.
  • Ich wurde eigentlich zu dieser Lösung geleitet, nachdem ich diese Frage zu StackOverflow gestellt hatte.
  • Ich war auch erfreut zu erfahren, dass die gesuchte Funktionalität – das Anwenden einer Funktion auf eine Teilmenge von Spalten mit .SDcols unter Beibehaltung der unberührten Spalten – als Feature Request hinzugefügt wurde.

dt <- data.table(mtcars)[,1:5, with=F]
für (j in c(1L,2L,4L))) set(dt, j=j, value=-dt[[j]]) # ganze Zahlen, die ‚L‘ verwenden, die für Effizienz übergeben werden.
für (j in c(3L,5L)) set(dt, j=j, value=paste0(dt[[j]],‘!!‘))))
Kopf(dt)
## mpg cyl disp hp drat
## 1: -21.0 -6 160!! -110 3.9!!
## 2: -21.0 -6 160!! -110 3.9!!
## 3: -22.8 -4 108!! -93 3.85!!
## 4: -21.4 -6 258!! -110 3.08!!
## 5: -18.7 -8 360!! -175 3.15!!
## 6: -18.1 -6 225!! -105 2.76!!
Verwendung der Verschiebung zum Führen/Lagern von Vektoren und Listen
Beachten Sie, dass diese Funktion nur in der Version 1.9.5 (derzeit auf Github, nicht CRAN) verfügbar ist. Base R verfügt überraschend nicht über großartige Werkzeuge für den Umgang mit Leads/Lags von Vektoren, mit denen die meisten sozialwissenschaftlichen Statistikprogramme (Stata, SAS, sogar FAME, das ich in meinen prägenden Datenjahren verwendet habe) ausgestattet sind.
dt <- data.table(mtcars)[,.(mpg, cyl)]]
dt[,mpg_lag1:=shift(mpg, 1)]]
dt[,mpg_forward1:=shift(mpg, 1, type=’lead‘)]]]
Kopf(dt)
## mpg cyl mpg_lag1 mpg_forward1
## 1: 21.0 6 NA 21.0
## 2: 21.0 6 21.0 22.8
## 3: 22.8 4 21.0 21.4
## 4: 21.4 6 22.8 18.7
## 5: 18.7 8 21.4 18.1
## 6: 18.1 6 18.7 14.3
verschieben mit by
# Erstellen von Daten
n <- 30
dt <- data.table(
date=rep(seq(as.date(‚2010-01-01‘), as.date(‚2015-01-01′), by=’year‘), n/6),
ind=rpois(n, 5),
entity=sort(rep(letters[1:5], n/5))
)

setkey(dt, entity, date) # wichtig für die Bestellung
dt[,indpct_fast:=(ind/shift(ind, 1))-1, by=entity]

Lagpad <- Funktion(x, k) c(rep(NA, k), x)[1:Länge(x)]
dt[,indpct_slow:=(ind/lagpad(ind, 1))-1, by=entity]

Kopf(dt, 10)
## 1: 2010-01-01-01 3 a NA NA NA
## 2: 2011-01-01 2 a -0.3333333 -0.3333333
## 3: 2012-01-01 5 a 1.5000000 1.5000000
## 4: 2013-01-01 4 a -0.2000000 -0.2000000
## 5: 2014-01-01 1 a -0.7500000 -0.7500000
## 6: 2015-01-01 5 a 4.0000000 4.0000000
## 7: 2010-01-01-01 2 b NA NA NA
## 8: 2011-01-01 6 b 2.0000000 2.0000000
## 9: 2012-01-01 8 b 0.3333333 0.3333333
## 10: 2013-01-01 9 b 0.1250000 0.1250000
Erstellen Sie mehrere Spalten mit := in einer Anweisung.
Dies ist nützlich, aber beachten Sie, dass die Spalten, an denen gearbeitet wird, Atomvektoren oder Listen sein müssen. Das heißt, sie müssen vor der Ausführung der Berechnung vorhanden sein.
Gebäudekolonnen, die auf andere Kolonnen in diesem Satz verweisen, müssen einzeln oder in Kettenform erstellt werden.
dt <- data.table(mtcars)[,.(mpg, cyl)]]
dt[,`:=`(avg=mittel(mpg), med=median(mpg), min=min(mpg)), by=cyl]
Kopf(dt)

## mpg cyl avg avg med min
## 1: 21.0 6 19.74286 19.7 17.8
## 2: 21.0 6 19.74286 19.7 17.8
## 3: 22.8 4 26.66364 26.0 21.4
## 4: 21.4 6 19.74286 19.7 17.8
## 5: 18.7 8 15.10000 15.2 10.4
## 6: 18.1 6 19.74286 19.7 17.8
Weisen Sie eine Spalte mit := benannt mit einem Zeichenobjekt zu.
Dies ist der empfohlene Weg, um eine neue Spalte zuzuordnen, deren Namen Sie bereits festgelegt und als Zeichen gespeichert haben. Umgeben Sie das Zeichenobjekt einfach in Klammern.
dt <- data.table(mtcars)[, .(cyl, mpg)]]

thing2 <-‚mpgx2“.
dt[,(thing2):=mpg*2]

Kopf(dt)
## cyl mpg mpg mpgx2
## 1: 6 21.0 42.0
## 2: 6 21.0 42.0
## 3: 4 22.8 45.6
## 4: 6 21.4 42.8
## 5: 8 18.7 37.4
## 6: 6 18.1 36.2
Dies ist eine alte (jetzt veraltete) Methode, die im Moment noch funktioniert. Nicht empfohlen.
thing3 <-‚mpgx3“.
dt[,thing3:=mpg*3, with=F]

Kopf(dt)
## cyl mpg mpg mpgx2 mpgx3
## 1: 6 21.0 42.0 63.0
## 2: 6 21.0 42.0 63.0
## 3: 4 22.8 45.6 68.4
## 4: 6 21.4 42.8 64.2
## 5: 8 18.7 37.4 56.1
## 6: 6 18.1 36.2 54.3
2. BY

Berechnen einer Funktion über eine Gruppe (mit by), wobei jede Entität in einer zweiten Kategorie ausgeschlossen wird.

Dieser Titel macht wahrscheinlich nicht sofort viel Sinn. Lassen Sie mich erklären, was ich berechnen werde und warum an einem Beispiel. Wir wollen das mpg jedes Autos mit dem durchschnittlichen mpg der Autos in der gleichen Klasse (die gleiche Anzahl von Zylindern) vergleichen. Wir wollen jedoch nicht den Gruppenmittelwert verzerren, indem wir das Auto einbeziehen, das wir mit dem Durchschnitt in diesem Durchschnitt vergleichen wollen.

Diese Annahme erscheint in diesem Beispiel nicht sinnvoll, sondern geht davon aus, dass gear+cyl die Autos eindeutig identifiziert. In dem realen Projekt, in dem ich mit diesem Problem konfrontiert war, berechnete ich einen Indikator für einen Gutachter im Verhältnis zum Durchschnitt aller anderen Gutachter in ihrer zip3. (Zyl. war wirklich Postleitzahl und Ausrüstung war der Ausweis des Gutachters).
METHODE 1: In-line
0,a Verzerrter Mittelwert: einfacher Mittelwert durch Zyl.
Wir wollen jedoch für jede Reihe wissen, was der Mittelwert unter all den anderen Autos mit der gleichen Anzahl von Zyklen ist, mit Ausnahme dieses Autos.

dt <- data.table(mtcars)[,.(cyl, gear, mpg)]]
dt[, mpg_biased_mean:=mean(mpg), by=cyl]
Kopf(dt)
## cyl gear mpg mpg mpg_biased_mean
## 1: 6 4 21.0 19.74286
## 2: 6 4 21.0 19.74286
## 3: 4 4 22.8 26.66364
## 4: 6 3 21.4 19.74286
## 5: 8 3 18.7 15.10000
## 6: 6 3 18.1 19.74286
1.a.GRP ohne Einstellschlüssel
dt[, dt[!gear %in% unique(dt$gear)[.GRP], mean(mpg), by=cyl], by=gear] #unverzerrtes Mittel
## Zahnrad-Zylinder V1
## 1: 4 6 19.73333
## 2: 4 8 15.10000
## 3: 4 4 25.96667
## 4: 3 6 19.74000
## 5: 3 4 27.18000
## 6: 3 8 15.40000
## 7: 5 6 19.75000
## 8: 5 4 26.32222
## 9: 5 8 15.05000
# überprüfen
dt[gear!=4 & cyl===6, mean(mpg)]]
## [1] 19.73333

Aktualisierung 24.9.2015: Per Matt Dowle’s Kommentare, dies funktioniert auch mit etwas weniger Code. Für mein einfaches Beispiel gab es auch einen marginalen Geschwindigkeitsgewinn. Die Zeitersparnis gegenüber der.GRP-Methode wird mit der Komplexität des Problems wahrscheinlich zunehmen.
dt[, dt[!gear %in% .BY[[1]], mean(mpg), by=cyl], by=gear] #unverzerrtes Mittel
## Zahnrad-Zylinder V1
## 1: 4 6 19.73333
## 2: 4 8 15.10000
## 3: 4 4 25.96667
## 4: 3 6 19.74000
## 5: 3 4 27.18000
## 6: 3 8 15.40000
## 7: 5 6 19.75000
## 8: 5 4 26.32222
## 9: 5 8 15.05000
1.b Wie 1.a, aber etwas schneller.

uid <- unique(dt$gear)
dt[, dt[!gear %in% (uid[.GRP]), mean(mpg), by=cyl], by=gear][order(cyl, gear)] #unverzerrtes Mittel
## Zahnrad-Zylinder V1
## 1: 3 4 27.18000
## 2: 4 4 25.96667
## 3: 5 4 26.32222
## 4: 3 6 19.74000
## 5: 4 6 19.73333
## 6: 5 6 19.75000
## 7: 3 8 15.40000
## 8: 4 8 15.10000
## 9: 5 8 15.05000
Warum funktioniert das?
# 1.a es auseinanderziehen mit .GRP
dt[, .GRP, by=cyl]
## Zylinder aus GFK
## 1: 6 1
## 2: 4 2
## 3: 8 3
dt[, .(.GRP, unique(dt$gear)[.GRP]), by=cyl]
## Zylinder GFK V2
## 1: 6 1 4
## 2: 4 2 3
## 3: 8 3 5
dt[,dt[, .(.GRP, unique(dt$gear)[.GRP]), by=cyl], by=gear]
## Zahnrad-Zylinder GFK V2
## 1: 4 6 1 4
## 2: 4 4 2 3
## 3:

## 3: 4 8 3 5
## 4: 3 6 1 4
## 5: 3 4 2 3
## 6: 3 8 3 5
## 7: 5 6 1 4
## 8: 5 4 2 3
## 9: 5 8 3 5
1.b Einstellschlüssel
setkey(dt, gear)
uid <- unique(dt$gear)
dt[, dt[!..(uid[.GRP]), mean(mpg), by=cyl], by=gear] #unverzerrtes Mittel
## Zahnrad-Zylinder V1
## 1: 3 6 19.74000
## 2: 3 4 27.18000
## 3: 3 8 15.40000
## 4: 4 6 19.73333
## 5: 4 8 15.10000
## 6: 4 4 25.96667
## 7: 5 6 19.75000
## 8: 5 8 15.05000
## 9: 5 4 26.32222
mean(dt[cyl===4 & gear!=3,mpg]) # testung
## [1] 27.18
mean(dt[cyl===6 & gear!=3,mpg]) # testing # testing
## [1] 19.74
METHODE 2: Verwendung von {} und .SD
{} wird verwendet, um Zwischenoperationen zu unterdrücken.
Aufbauen

Hier gibt es keine Überraschungen.
dt[, .SD[, mean(mpg)], by=gear] # wie `dt[, mean(mpg), by=gear]`
## Gang V1
## 1: 3 16.10667
## 2: 4 24.53333
## 3: 5 21.38000
dt[, .SD[, mean(mpg), by=cyl], by=gear] # wie `dt[, mean(mpg), by=.(cyl, by=gear)]`
## Zahnrad-Zylinder V1
## 1: 3 6 19.750
## 2: 3 8 15.050
## 3: 3 4 21.500
## 4: 4 6 19.750
## 5: 4 4 26.925
## 6: 5 4 28.200
## 7: 5 8 15.400
## 8: 5 6 19.700
Verschachtelte data.tables und durch Anweisungen
Dieser Chunk zeigt, was mit zweien passiert, indem Anweisungen in zwei verschiedene data.tables verschachtelt sind. Nur zu Erklärungszwecken – nicht notwendig für unsere Aufgabe. n zählt die Anzahl der Fahrzeuge in diesem Zylinder, N zählt die Anzahl der Fahrzeuge pro Zylinder und Getriebe.

dt[,{
vbar = sum(mpg)
n = .N
.SD[,.(n, .N, sum_in_gear_cyl=sum(mpg), sum_in_cyl=vbar), by=gear]
} , by=cyl]
## Zylindergetriebe n N Summe_in_Getriebe_Zylinder Summe_in_Zylindern
## 1: 6 3 7 2 39.5 138.2
## 2: 6 4 7 4 79.0 138.2
## 3: 6 5 7 1 19.7 138.2
## 4: 8 3 14 12 180.6 211.4
## 5: 8 5 14 2 30.8 211.4
## 6: 4 3 11 1 21.5 293.3
## 7: 4 4 11 8 215.4 293.3
## 8: 4 5 11 2 56.4 293.3
dt[,sum(mpg), by=cyl] # test
## Zylinder V1
## 1: 6 138.2
## 2: 8 211.4
## 3: 4 293.3
Berechnung des „unvoreingenommenen Mittelwerts“.
Dies geschieht in einer Summentabelle. Dies müsste, wenn gewünscht, wieder auf dt zusammengeführt werden.

dt[,{
vbar = mean(mpg)
n = .N
.SD[,(n*vbar-sum(mpg))/(n-.N),by=gear]
} , by=cyl]
## Zylindergetriebe V1
## 1: 6 3 19.74000
## 2: 6 4 19.73333
## 3: 6 5 19.75000
## 4: 8 3 15.40000
## 5: 8 5 15.05000
## 6: 4 3 27.18000
## 7: 4 4 25.96667
## 8: 4 5 26.32222
METHODE 3: Superschnelle Mittelwertberechnung
Nichtfunktionaler direkter Weg
Verwenden eines vektorisierten Ansatzes zum Berechnen des unvoreingenommenen Mittelwerts für jede Kombination von Zahnrad und Zylinder. Mechanisch berechnet es den „voreingenommenen Durchschnitt“ für alle Fahrzeuge nach Zylindern und subtrahiert dann den Anteil der Fahrzeuge mit der Kombination aus Getriebe und Zylinder, die wir vom Durchschnitt ausschließen wollen, und addiert diesen Anteil. Dann extrapolieren Sie diesen reduzierten Mittelwert.

dt <- data.table(mtcars)[,.(mpg,cyl,gear)]]
dt[,`:=`(avg_mpg_cyl=mittel(mpg), Ncyl=.N), by=cyl]
dt[,`:=`(Ncylgear=.N, avg_mpg_cyl_gear=mean(mpg))), by=.(cyl, gear)]]
dt[,unbmean:=(avg_mpg_cyl*Ncyl-(Ncylgear*avg_mpg_cyl_gear))/(Ncyl-Ncylgear)]]]
setkey(dt, cyl, cyl, gear)
Kopf(dt)
## mpg cyl gear avg_mpg_cyl Ncyl Ncyl Ncylgear avg_mpg_cyl_gear unbemittelt
## 1: 21.5 4 3 26.66364 11 1 21.500 27.18000
## 2: 22.8 4 4 26.66364 11 8 26.925 25.96667
## 3: 24.4 4 4 26.66364 11 8 26.925 25.96667
## 4: 22.8 4 4 26.66364 11 8 26.925 25.96667
## 5: 32.4 4 4 26.66364 11 8 26.925 25.96667
## 6: 30.4 4 4 26.66364 11 8 26.925 25.96667
Verpacken des untenstehenden Codes in eine Funktion
leaveOneOutMean <- Funktion(dt, ind, bybig, bysmall) {
dtmp <- copy(dt) # copy, um das ursprüngliche dt Objekt mit Zwischenzuweisungen nicht zu verändern.
dtmp <- dtmp[is.na(get(ind))==F,]
dtmp[,`:=`(avg_ind_big=mean(get(ind)), Nbig=.N), by=.(get(bybig))]]
dtmp[,`:=`(Nbigsmall=.N, avg_ind_big_small=mean(get(ind)))), by=.(get(bybig), get(bysmall))]]]
dtmp[,unbmean:=(avg_ind_big*Nbig-(Nbigsmall*avg_ind_big_klein_klein))/(Nbig-Nbigklein)]]]
return(dtmp[,unbmean]))
}

dt <- data.table(mtcars)[,.(mpg,cyl,gear)]]
dt[,unvoreingenommen_mean:=leaveOneOutMean(.SD, ind=’mpg‘, bybig=’cyl‘, bysmall=’gear‘)]]]
dt[,bias_mean:=mean(mpg), by=cyl]
Kopf(dt)
## mpg cyl Zahnrad unvoreingenommen_mittel voreingenommen_mittel
## 1: 21.0 6 4 19.73333 19.74286
## 2: 21.0 6 4 19.73333 19.74286
## 3: 22.8 4 4 25.96667 26.66364
## 4: 21.4 6 3 19.74000 19.74286
## 5: 18.7 8 3 15.40000 15.10000
## 6: 18.1 6 3 19.74000 19.74286

Geschwindigkeitskontrolle

Das Verfahren 3 ist etwa 100x schneller als die beiden anderen. Großartig für diese enge Aufgabe mit der eingebauten Vektorisierung, aber weniger verallgemeinerbar; Die beiden anderen Methoden ermöglichen es, jede Funktion zu übergeben.
dt <- data.table(mtcars)
dt <- dt[sample(1:.N, 100000, replace=T), ] # Erhöhung der Zeilenzahl in mtcars

dt$gear <- sample(1:300, nrow(dt), replace=T) # Hinzufügen von mehr Kateogorien
Verfahren 3:
system.time(dt[,unvoreingenommen_mittel_vektorisiert:=leaveOneOutMean(.SD, ind=’mpg‘, bybig=’cyl‘, bysmall=’gear‘)]))
##Benutzersystem ist abgelaufen
## 0.033 0.003 0.035
Verfahren 2:
system.time(tmp <- dt[,dt[!gear %in% unique(dt$gear)[.GRP], mean(mpg), by=cyl], by=gear] ) )
##Benutzersystem ist abgelaufen
## 3.709 0.359 4.069
Verfahren 1:
uid <- unique(dt$gear)
system.time(dt[, dt[!gear %in% (uid[.GRP]), mean(mpg), by=cyl], by=gear][order(cyl, gear)]))
##Benutzersystem ist abgelaufen
## 3.345 0.331 3.677

Schlüssel für Schlüssel, die sich daraus ergebende aggregierte Tabelle Ohne Keyby
Kategorien sind nicht sortiert
## devtools::install_github(‚brooksandrew/Rsenal‘)
library(‚Rsenal‘) # Grabbing Tiefenbin-Funktion
tmp <- dt[, .(N=.N, sum=sum(vs), mean=mean(vs)/.N), by=depthbin(mpg, 5, labelOrder=T)]]
tmp
## Tiefe N Summe Mittelwert
## 1: (15.2,17.8] 2/5 15372 3131 1.325020e-05
## 2: (17.8,21] 3/5 21839 6204 1.300787e-05
## 3: [10.4,15.2] 1/5 25255 0 0.000000e+00
## 4: (21,24.4] 4/5 18817 18817 5.314343e-05
## 5: (24.4,33.9] 5/5 18717 15581 4.447571e-05
tmp[,barplot(mean, names=depthbin, las=2)]]

## [,1]
## [1,] 0.7
## [2,] 1.9
## [3,] 3.1
## [4,] 4.3
## [5,] 5.5
Mit Keyby
## devtools::install_github(‚brooksandrew/Rsenal‘)
Bibliothek(‚Rsenal‘)
tmp <- dt[, .(N=.N, sum=sum(vs), mean=mean(vs)/.N), keyby=depthbin(mpg, 5, labelOrder=T)]]
tmp
## Tiefe N Summe Mittelwert
## 1: [10.4,15.2] 1/5 25255 0 0.000000e+00
## 2: (15.2,17.8] 2/5 15372 3131 1.325020e-05
## 3: (17.8,21] 3/5 21839 6204 1.300787e-05
## 4: (21,24.4] 4/5 18817 18817 5.314343e-05
## 5: (24.4,33.9] 5/5 18717 15581 4.447571e-05
tmp[,barplot(mean, names=depthbin, las=2)]]

## [,1]
## [1,] 0.7
## [2,] 1.9
## [3,] 3.1
## [4,] 4.3
## [5,] 5.5
Verwenden von [1], [.N], setkey und by für innerhalb der Gruppenunterordnung
den höchsten Wert der Spalte A annehmen, wenn die Spalte B nach Gruppe am höchsten ist.
Max. qsec für jede Kategorie von Zylindern (das ist einfach)
dt <- data.table(mtcars)[, .(cyl, mpg, qsec)]]
dt[, max(qsec), by=cyl]
## Zylinder V1
## 1: 6 20.22
## 2: 4 22.90
## 3: 8 18.00
Wert von qsec, wenn mpg der höchste Wert pro Kategorie von Zylindern ist.
(das ist kniffliger)
setkey(dt, mpg)
dt[,qsec[.N], by=cyl]
## Zylinder V1
## 1: 8 17.05
## 2: 6 19.44
## 3: 4 19.90

  • Wert von qsec, wenn mpg der niedrigste pro Kategorie von Zylindern ist.
    dt[,qsec[1], by=cyl]
    ## Zylinder V1
    ## 1: 8 17.98
    ## 2: 6 18.90
    ## 3: 4 18.60
  • Wert von qsec, wenn mpg der Median pro Kategorie von Zylindern ist.
    dt[,qsec[round(.N/2)], by=cyl], by=cyl]
    ## Zylinder V1
    ## 1: 8 18.0
    ## 2: 6 15.5
    ## 3: 4 16.7
  • Teilmengenzeilen innerhalb der Anweisung
    V1 ist die Standardabweichung von mpg nach Zylindern.
  • V2 ist die Standardabweichung von mpg für nur die erste Hälfte von mpg.
  • dt <- data.table(mtcars)
  • setkey(dt,mpg)
  • dt[, .(sd(mpg), sd(mpg[1:round(.N/2)]))), by=cyl])
    ## cyl V1 V2 V2
    ## 1: 8 2.560048 2.0926174
    ## 2: 6 1.453567 0.8981462
    ## 3: 4 4.509828 1.7728508

3. FUNKTIONEN

Übergabe von Spaltennamen der data.table als Funktionsargumente
Methode 1: Keine Angebote, und Abreise + Ersatz
Dieser Weg erscheint mehr data.table-ish, weil er die Praxis beibehält, in den meisten Fällen keine Anführungszeichen für Variablennamen zu verwenden.
dt <- data.table(mtcars)[,.(cyl, mpg)]]
myfunc <- function(dt, v) {
v2=deparse(substitute(v))
dt[,v2, with=F][[1]]] # [[1]]] gibt einen Vektor anstelle einer data.table zurück.
}

myfunc(dt, mpg)
## [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2
## [15] 10.4 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4
## [29] 15.8 19.7 15.0 21.4
Methode 2: Angebote und erhalten Sie
Allerdings tendiere ich dazu, Spaltennamen als Zeichen (zitiert) zu verwenden und get jedes Mal zu verwenden, wenn ich auf diese Spalte verweise. Das kann ärgerlich sein, wenn Sie eine lange Funktion haben, die wiederholt auf Spaltennamen verweist, aber ich muss oft so wenige Zeilen Code mit data.table schreiben, es ist mir noch nicht so schrecklich unscheinbar erschienen.
dt <- data.table(mtcars)
myfunc <- function(dt, v) dt[,get(v)]]

myfunc(dt, ‚mpg‘)
## [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2
## [15] 10.4 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4
## [29] 15.8 19.7 15.0 21.4
Vorsicht vor dem Scoping in der data.table
data.frame way
Wenn Sie innerhalb einer Funktion, die in der globalen Umgebung existiert, etwas zu einem data.frame hinzufügen, wirkt sich dies nicht auf dieses Objekt in der globalen Umgebung aus, es sei denn, Sie kehren zurück und weisen es als solches neu zu oder Sie verwenden den Operator <<<-.
df <- mtcars[,c(‚cyl‘, ‚mpg‘)]]
add_column_df <- function(df) {
df$addcol1<-‚hier in func!‘.
df$addcol2 <<<-‚in glob env!‘.
rückgabe(df)
}

Wenn wir die Funktion aufrufen, sehen wir addcol1 in der Ausgabe. Aber nicht addcol2. Das liegt daran, dass es dem df in der globalen Umgebung eine Ebene höher hinzugefügt wurde.
head(add_column_df(df)))
## cyl mpg addcol1
## Mazda RX4 6 21.0 hier in func!
## Mazda RX4 Wag 6 21.0 hier in func!
## Datsun 710 4 22.8 hier in func!
## Hornet 4 Drive 6 21.4 hier in func!

## Hornet Sportabout 8 18.7 hier in func!
## Valiant 6 18.1 hier in func!
Hier ist addcol2, aber nicht addcol.
Kopf(df)
## cyl mpg addcol2
## Mazda RX4 6 21.0 in glob env!
## Mazda RX4 Wag 6 21.0 in glob env!
## Datsun 710 4 22.8 in glob env!
## Hornet 4 Drive 6 21.4 in glob env!
## Hornet Sportabout 8 18.7 in glob env!
## Valiant 6 18.1 in glob env!
data.table Weg

Im Gegensatz zu data.frame fügt der Operator := eine Spalte sowohl dem in der globalen Umgebung lebenden als auch in der Funktion verwendeten Objekt hinzu. Ich denke, das liegt daran, dass diese Objekte tatsächlich das gleiche Objekt sind. data.table verkürzt die Rechenzeit, indem es keine Kopien erstellt, es sei denn, es wird ausdrücklich darauf hingewiesen.
dt <- data.table(mtcars)
add_column_dt <- function(dat) {
dat[,addcol:=’kleben_an_dt!‘] # hits dt in glob env
return(dat)
}
head(add_column_dt(dt))) # addcol here
## mpg cyl disp hp drat wt qsec vs. am gear carb addcol
## 1: 21.0 6 160 110 110 3.90 2.620 16.46 0 1 4 4 kleben_an_dt!
## 2: 21.0 6 160 110 110 3.90 2.875 17.02 0 1 4 4 kleben_an_dt!
## 3: 22.8 4 108 93 3.85 2.320 18.61 1 1 1 4 1 kleben_an_dt!
## 4: 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 kleben_an_dt!
## 5: 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 kleben_an_dt!
## 6: 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 Verkleben_auf_dt!
head(dt) # addcol auch hier
## mpg cyl disp hp drat wt qsec vs. am gear carb addcol
## 1: 21.0 6 160 110 110 3.90 2.620 16.46 0 1 4 4 kleben_an_dt!
## 2: 21.0 6 160 110 110 3.90 2.875 17.02 0 1 4 4 kleben_an_dt!
## 3: 22.8 4 108 93 3.85 2.320 18.61 1 1 1 4 1 kleben_an_dt!
## 4: 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 kleben_an_dt!
## 5: 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 kleben_an_dt!
## 6: 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 Verkleben_auf_dt!

So etwas wie diese Umbenennung der lokalen Version mit Kopie umgeht dieses Verhalten, ist aber wahrscheinlich etwas weniger effizient (und elegant). Ich vermute, dass es einen saubereren und/oder schnelleren Weg gibt, dies zu tun: Halten Sie einige Variablen lokal für die Funktion, während Sie andere Spalten persistieren und zurückgeben.
dt <- data.table(mtcars)
add_column_dt <- function(dat) {
datloc <- copy(dat)
datloc[,addcol:=’not sticking_to_dt!‘] # trifft dt in glob env
return(datloc)
}

head(add_column_dt(dt))) # addcol here
## mpg cyl disp hp drat wt qsec vs. am gear carb addcol
## 1: 21.0 6 160 110 110 3.90 2.620 16.46 0 1 4 4 nicht haftend_an_dt!
## 2: 21.0 6 160 110 110 3.90 2.875 17.02 0 1 4 4 4 nicht haftend_an_dt!
## 3: 22.8 4 108 93 3.85 2.320 18.61 1 1 1 4 1 nicht klebend_an_dt!
## 4: 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 nicht haftend_an_dt!
## 5: 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 nicht haftend_an_dt!
## 6: 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 nicht klebend_an_dt!
head(dt) # addcol nicht hier
## mpg cyl disp hp drat wt qsec vs. am gear carb
## 1: 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## 2: 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## 3: 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## 4: 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## 5: 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## 6: 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

4. DRUCKEN

Drucke data.table mit [].
Nichts bahnbrechendes hier, sondern ein kleines, vielseitiges Stück Funktionalität. In der data.frame-Welt wird durch das Umschließen eines Ausdrucks in () die Ausgabe an die Konsole gedruckt. Dies funktioniert auch mit data.table, aber es gibt noch einen anderen Weg. In der data.table wird dies durch Anhängen von [] an das Ende des Ausdrucks erreicht. Ich finde das nützlich, denn wenn ich an der Konsole forsche, beschließe ich normalerweise nicht, die Ausgabe zu drucken, bis ich fast fertig bin und ich bereits am Ende des von mir geschriebenen Ausdrucks stehe.
# data.frame Druckweise nach einer Zuweisung
df <- head(mtcars) # wird nicht gedruckt
(df <- head(mtcars)) # does print

## mpg cyl disp hp drat wt qsec vs. am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 4 4
## Datsun 710 22,8 4 108 93 3,85 2,320 18,61 1 1 1 4 1 4 1
## Hornet 4 Antrieb 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 0 3 1
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 0 3 2 2 2
## Tapfer 18,1 6 225 105 2,76 3,460 20,22 1 0 3 1 1
# data.table Druckmethode nach einer Zuweisung
dt <- data.table(head(mtcars)) # druckt nicht
dt[,hp2wt:=hp/wt][][] # wird gedruckt
## mpg cyl disp hp drat wt qsec vs. am Getriebe carb hp2wt
## 1: 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 41.98473
## 2: 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 38.26087
## 3: 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 40.08621
## 4: 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 34.21462

## 5: 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 50.87209
## 6: 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 30.34682
Ausgabe ausblenden von := mit knitritritr
Früher druckten Zuweisungen mit dem Operator := das Objekt an die Konsole, wenn Dokumente mit Strickerei und Rmarkdown gestrickt wurden. Dies ist in der data.table v1.9.5 behoben. Zum Zeitpunkt meines Schreibens ist dies jedoch derzeit auf CRAN nicht verfügbar…. nur Github. Für 1.9.4 Benutzer bietet dieser StackOverflow-Post einige Hacky-Lösungen. Dieser Ansatz der geringsten Impedanz, den ich gefunden habe, war es, den Ausdruck einfach in unsichtbar zu wickeln. Andere Lösungen ändern die Art und Weise, wie Sie data.table verwenden, was mir nicht gefiel.