Ich sehe viele Fragen und Antworten zu order
und sort
. Gibt es etwas, das Vektoren oder Datenrahmen in Gruppierungen sortiert (wie Quartile oder Dezile)? Ich habe eine "manuelle" Lösung, aber es gibt wahrscheinlich eine bessere Lösung, die in Gruppen getestet wurde.
Hier ist mein Versuch:
temp <- data.frame(name=letters[1:12], value=rnorm(12), quartile=rep(NA, 12))
temp
# name value quartile
# 1 a 2.55118169 NA
# 2 b 0.79755259 NA
# 3 c 0.16918905 NA
# 4 d 1.73359245 NA
# 5 e 0.41027113 NA
# 6 f 0.73012966 NA
# 7 g -1.35901658 NA
# 8 h -0.80591167 NA
# 9 i 0.48966739 NA
# 10 j 0.88856758 NA
# 11 k 0.05146856 NA
# 12 l -0.12310229 NA
temp.sorted <- temp[order(temp$value), ]
temp.sorted$quartile <- rep(1:4, each=12/4)
temp <- temp.sorted[order(as.numeric(rownames(temp.sorted))), ]
temp
# name value quartile
# 1 a 2.55118169 4
# 2 b 0.79755259 3
# 3 c 0.16918905 2
# 4 d 1.73359245 4
# 5 e 0.41027113 2
# 6 f 0.73012966 3
# 7 g -1.35901658 1
# 8 h -0.80591167 1
# 9 i 0.48966739 3
# 10 j 0.88856758 4
# 11 k 0.05146856 2
# 12 l -0.12310229 1
Gibt es einen besseren (saubereren / schnelleren / einzeiligen) Ansatz? Vielen Dank!
cut()
hat ein Argument,labels
das verwendet werden kann, damit Sie diefactor()
Zeile nicht benötigen - fügen Sie einfachlabels = 1:4
dencut()
Aufruf Ihrer ersten Zeile hinzu.findInterval
wie oben verwendet scheint in diesem Fall besser zu seinprobs=c((0:9)/10), Inf)
mit findInterval oderprobs=seq(0,1, by=0.1))
zum Schneiden verwenden. Ein wichtiger Unterschied zwischen diesen beiden Funktionen besteht darin, dass die Intervalle standardmäßig links fürfindInterval
und rechts für geschlossen sindcut
. Guter Punkt über NAs; Wie sum oder main oder max sollte wahrscheinlich na.rm = TRUE für addierenquantile
.Es gibt eine praktische
ntile
Funktion im Paketdplyr
. Es ist flexibel in dem Sinne, dass Sie sehr einfach die Anzahl der * Kacheln oder "Bins" definieren können, die Sie erstellen möchten.Laden Sie das Paket (installieren Sie es zuerst, wenn Sie es nicht haben) und fügen Sie die Quartilspalte hinzu:
library(dplyr) temp$quartile <- ntile(temp$value, 4)
Oder wenn Sie die dplyr-Syntax verwenden möchten:
temp <- temp %>% mutate(quartile = ntile(value, 4))
Ergebnis in beiden Fällen ist:
temp # name value quartile #1 a -0.56047565 1 #2 b -0.23017749 2 #3 c 1.55870831 4 #4 d 0.07050839 2 #5 e 0.12928774 3 #6 f 1.71506499 4 #7 g 0.46091621 3 #8 h -1.26506123 1 #9 i -0.68685285 1 #10 j -0.44566197 2 #11 k 1.22408180 4 #12 l 0.35981383 3
Daten:
Beachten Sie, dass Sie die Spalte "Quartil" nicht im Voraus erstellen und verwenden müssen
set.seed
, um die Randomisierung reproduzierbar zu machen:set.seed(123) temp <- data.frame(name=letters[1:12], value=rnorm(12))
quelle
ntile
(einschließlich niedrigster, höchster, Bindungen) verwendet werdentemp <- temp %>% mutate(quartile = cut(x = ntile(value, 100), breaks = seq(25,100,25) , include.lowest = TRUE, right = FALSE , labels = FALSE))
Ich werde die
data.table
Version für alle anderen hinzufügen, die sie googeln (dh die Lösung von @ BondedDustdata.table
wurde in ein bisschen übersetzt und reduziert):library(data.table) setDT(temp) temp[ , quartile := cut(value, breaks = quantile(value, probs = 0:4/4), labels = 1:4, right = FALSE)]
Was ist viel besser (sauberer, schneller ) als das, was ich getan habe:
temp[ , quartile := as.factor(ifelse(value < quantile(value, .25), 1, ifelse(value < quantile(value, .5), 2, ifelse(value < quantile(value, .75), 3, 4))]
Beachten Sie jedoch, dass bei diesem Ansatz die Quantile unterschiedlich sein müssen, z. B. wenn dies fehlschlägt
rep(0:1, c(100, 1))
. Was in diesem Fall zu tun ist, ist offen, also überlasse ich es Ihnen.quelle
right = F
ist falsch. Der Maximalwert ist nicht nur nicht gruppiert, sondern Ihre Daten sind 1:21, der Median ist 11, sondern wird in die .75-Gruppe gruppiert.Sie können die
quantile()
Funktion verwenden, müssen jedoch bei der Verwendung mit Rundung / Präzision umgehencut()
. Damitset.seed(123) temp <- data.frame(name=letters[1:12], value=rnorm(12), quartile=rep(NA, 12)) brks <- with(temp, quantile(value, probs = c(0, 0.25, 0.5, 0.75, 1))) temp <- within(temp, quartile <- cut(value, breaks = brks, labels = 1:4, include.lowest = TRUE))
Geben:
> head(temp) name value quartile 1 a -0.56047565 1 2 b -0.23017749 2 3 c 1.55870831 4 4 d 0.07050839 2 5 e 0.12928774 3 6 f 1.71506499 4
quelle
Tut mir leid, dass ich etwas zu spät zur Party komme. Ich wollte meinen Einzeiler mit hinzufügen,
cut2
da ich max / min für meine Daten nicht kannte und wollte, dass die Gruppen identisch groß sind. Ich habe über cut2 in einer Ausgabe gelesen, die als Duplikat markiert war (Link unten).library(Hmisc) #For cut2 set.seed(123) #To keep answers below identical to my random run temp <- data.frame(name=letters[1:12], value=rnorm(12), quartile=rep(NA, 12)) temp$quartile <- as.numeric(cut2(temp$value, g=4)) #as.numeric to number the factors temp$quartileBounds <- cut2(temp$value, g=4) temp
Ergebnis:
> temp name value quartile quartileBounds 1 a -0.56047565 1 [-1.265,-0.446) 2 b -0.23017749 2 [-0.446, 0.129) 3 c 1.55870831 4 [ 1.224, 1.715] 4 d 0.07050839 2 [-0.446, 0.129) 5 e 0.12928774 3 [ 0.129, 1.224) 6 f 1.71506499 4 [ 1.224, 1.715] 7 g 0.46091621 3 [ 0.129, 1.224) 8 h -1.26506123 1 [-1.265,-0.446) 9 i -0.68685285 1 [-1.265,-0.446) 10 j -0.44566197 2 [-0.446, 0.129) 11 k 1.22408180 4 [ 1.224, 1.715] 12 l 0.35981383 3 [ 0.129, 1.224)
Ähnliches Problem, in dem ich ausführlich über cut2 gelesen habe
quelle
Die Anpassung
dplyr::ntile
andata.table
Optimierungen bietet eine schnellere Lösung.library(data.table) setDT(temp) temp[order(value) , quartile := floor( 1 + 4 * (.I-1) / .N)]
Wahrscheinlich nicht als sauberer zu qualifizieren, aber es ist schneller und einzeilig.
Timing bei größerem Datensatz
Vergleich dieser Lösung mit
ntile
undcut
fürdata.table
wie von @docendo_discimus und @MichaelChirico vorgeschlagen.library(microbenchmark) library(dplyr) set.seed(123) n <- 1e6 temp <- data.frame(name=sample(letters, size=n, replace=TRUE), value=rnorm(n)) setDT(temp) microbenchmark( "ntile" = temp[, quartile_ntile := ntile(value, 4)], "cut" = temp[, quartile_cut := cut(value, breaks = quantile(value, probs = seq(0, 1, by=1/4)), labels = 1:4, right=FALSE)], "dt_ntile" = temp[order(value), quartile_ntile_dt := floor( 1 + 4 * (.I-1)/.N)] )
Gibt:
Unit: milliseconds expr min lq mean median uq max neval ntile 608.1126 647.4994 670.3160 686.5103 691.4846 712.4267 100 cut 369.5391 373.3457 375.0913 374.3107 376.5512 385.8142 100 dt_ntile 117.5736 119.5802 124.5397 120.5043 124.5902 145.7894 100
quelle
temp$quartile <- ceiling(sapply(temp$value,function(x) sum(x-temp$value>=0))/(length(temp$value)/4))
quelle
Ich möchte eine Version vorschlagen, die robuster zu sein scheint, da ich bei der Verwendung
quantile()
der Unterbrechungsoptioncut()
in meinem Datensatz auf viele Probleme gestoßen bin. Ich benutze dientile
Funktion vonplyr
, aber es funktioniert auchecdf
als Eingabe.temp[, `:=`(quartile = .bincode(x = ntile(value, 100), breaks = seq(0,100,25), right = TRUE, include.lowest = TRUE) decile = .bincode(x = ntile(value, 100), breaks = seq(0,100,10), right = TRUE, include.lowest = TRUE) )] temp[, `:=`(quartile = .bincode(x = ecdf(value)(value), breaks = seq(0,1,0.25), right = TRUE, include.lowest = TRUE) decile = .bincode(x = ecdf(value)(value), breaks = seq(0,1,0.1), right = TRUE, include.lowest = TRUE) )]
Ist das korrekt?
quelle
Probieren Sie diese Funktion aus
getQuantileGroupNum <- function(vec, group_num, decreasing=FALSE) { if(decreasing) { abs(cut(vec, quantile(vec, probs=seq(0, 1, 1 / group_num), type=8, na.rm=TRUE), labels=FALSE, include.lowest=T) - group_num - 1) } else { cut(vec, quantile(vec, probs=seq(0, 1, 1 / group_num), type=8, na.rm=TRUE), labels=FALSE, include.lowest=T) } }
> t1 <- runif(7) > t1 [1] 0.4336094 0.2842928 0.5578876 0.2678694 0.6495285 0.3706474 0.5976223 > getQuantileGroupNum(t1, 4) [1] 2 1 3 1 4 2 4 > getQuantileGroupNum(t1, 4, decreasing=T) [1] 3 4 2 4 1 3 1
quelle
Es gibt möglicherweise einen schnelleren Weg, aber ich würde tun:
a <- rnorm(100) # Our data q <- quantile(a) # You can supply your own breaks, see ?quantile # Define a simple function that checks in which quantile a number falls getQuant <- function(x) { for (i in 1:(length(q)-1)) { if (x>=q[i] && x<q[i+1]) break; } i } # Apply the function to the data res <- unlist(lapply(as.matrix(a), getQuant))
quelle