Hinzufügen einer Spalte zu einem data.frame

115

Ich habe den data.frame unten. Ich möchte eine Spalte hinzufügen, die meine Daten gemäß Spalte 1 ( h_no) so klassifiziert , dass die erste Reihe von h_no 1,2,3,4 Klasse 1, die zweite Reihe von h_no(1 bis 7) Klasse 2 usw. ist. wie in der letzten Spalte angegeben.

h_no  h_freq  h_freqsq
1     0.09091 0.008264628 1
2     0.00000 0.000000000 1
3     0.04545 0.002065702 1
4     0.00000 0.000000000 1  
1     0.13636 0.018594050 2
2     0.00000 0.000000000 2
3     0.00000 0.000000000 2
4     0.04545 0.002065702 2
5     0.31818 0.101238512 2
6     0.00000 0.000000000 2
7     0.50000 0.250000000 2 
1     0.13636 0.018594050 3 
2     0.09091 0.008264628 3
3     0.40909 0.167354628 3
4     0.04545 0.002065702 3
Susanne Dreisigacker
quelle

Antworten:

155

Sie können Ihren Daten mithilfe verschiedener Techniken eine Spalte hinzufügen. Die folgenden Anführungszeichen stammen aus dem Abschnitt "Details" des entsprechenden Hilfetextes [[.data.frame.

Datenrahmen können in verschiedenen Modi indiziert werden. Wenn [und [[mit einem einzelnen Vektorindex ( x[i]oder x[[i]]) verwendet werden, indizieren sie den Datenrahmen wie eine Liste.

my.dataframe["new.col"] <- a.vector
my.dataframe[["new.col"]] <- a.vector

Die data.frame-Methode für $wird xals Liste behandelt

my.dataframe$new.col <- a.vector

Wenn [und [[mit zwei Indizes ( x[i, j]und x[[i, j]]) verwendet werden, wirken sie wie die Indizierung einer Matrix

my.dataframe[ , "new.col"] <- a.vector

Da die Methode für data.framedavon ausgeht, dass Sie Spalten meinen, wenn Sie nicht angeben, ob Sie mit Spalten oder Zeilen arbeiten.


In Ihrem Beispiel sollte dies funktionieren:

# make some fake data
your.df <- data.frame(no = c(1:4, 1:7, 1:5), h_freq = runif(16), h_freqsq = runif(16))

# find where one appears and 
from <- which(your.df$no == 1)
to <- c((from-1)[-1], nrow(your.df)) # up to which point the sequence runs

# generate a sequence (len) and based on its length, repeat a consecutive number len times
get.seq <- mapply(from, to, 1:length(from), FUN = function(x, y, z) {
            len <- length(seq(from = x[1], to = y[1]))
            return(rep(z, times = len))
         })

# when we unlist, we get a vector
your.df$group <- unlist(get.seq)
# and append it to your original data.frame. since this is
# designating a group, it makes sense to make it a factor
your.df$group <- as.factor(your.df$group)


   no     h_freq   h_freqsq group
1   1 0.40998238 0.06463876     1
2   2 0.98086928 0.33093795     1
3   3 0.28908651 0.74077119     1
4   4 0.10476768 0.56784786     1
5   1 0.75478995 0.60479945     2
6   2 0.26974011 0.95231761     2
7   3 0.53676266 0.74370154     2
8   4 0.99784066 0.37499294     2
9   5 0.89771767 0.83467805     2
10  6 0.05363139 0.32066178     2
11  7 0.71741529 0.84572717     2
12  1 0.10654430 0.32917711     3
13  2 0.41971959 0.87155514     3
14  3 0.32432646 0.65789294     3
15  4 0.77896780 0.27599187     3
16  5 0.06100008 0.55399326     3
Roman Luštrik
quelle
Was ist der Unterschied zwischen den letzten beiden Methoden zum Hinzufügen einer Spalte?
Huon
2
@ huon-dbaupp Die Methode mit einem Komma ist explizit und funktioniert auch mit Matrizen, während die letzte nur mit data.frames funktioniert. Wenn kein Komma angegeben ist, geht R davon aus, dass Sie Spalten meinen.
Roman Luštrik
12

Einfach: Ihr Datenrahmen ist A.

b <- A[,1]
b <- b==1
b <- cumsum(b)

Dann erhalten Sie die Spalte b.

user1333396
quelle
Schön kurz. Ich würde nur das letzte Element so ändern, dass es nicht als cumsum(b) -> bErgebnis direkt als Spalte zum ursprünglichen Datenrahmen hinzugefügt wird, so etwas wie A$groups <- cumsum(b).
A5C1D2H2I1M1N2O1R2T1
cumsum(b)geben Sie einen Vektor der Länge 3, oder fehlt mir etwas?
Roman Luštrik
@ RomanLuštrik, siehe dbaupps Lösung, die erklärt, wie Cumsum in diesem Fall funktionieren würde.
A5C1D2H2I1M1N2O1R2T1
2
@ RomanLuštrik, Diese Lösung kann wirklich schön in einer einzigen Zeile umgeschrieben werden. Mit Ihren your.dfDaten können Sie einfach your.df$group = cumsum(your.df[, 1]==1)Ihre neue Gruppenspalte abrufen.
A5C1D2H2I1M1N2O1R2T1
7

Wenn ich die Frage richtig verstehe, möchten Sie erkennen, wenn sich die Frage h_nonicht erhöht, und dann die Frage erhöhen class. (Ich werde durchgehen, wie ich dieses Problem gelöst habe. Am Ende gibt es eine in sich geschlossene Funktion.)

Arbeiten

Die h_noSpalte ist uns momentan nur wichtig, daher können wir sie aus dem Datenrahmen extrahieren:

> h_no <- data$h_no

Wir wollen erkennen, wann h_nonicht steigt, was wir tun können, indem wir herausfinden, wann der Unterschied zwischen aufeinanderfolgenden Elementen entweder negativ oder null ist. R liefert die diffFunktion, die uns den Vektor der Unterschiede gibt:

> d.h_no <- diff(h_no)
> d.h_no
 [1]  1  1  1 -3  1  1  1  1  1  1 -6  1  1  1

Sobald wir das haben, ist es einfach, diejenigen zu finden, die nicht positiv sind:

> nonpos <- d.h_no <= 0
> nonpos
 [1] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
[13] FALSE FALSE

In R TRUEund FALSEsind im Grunde die gleichen wie 1und 0. Wenn wir also die kumulative Summe von erhalten nonpos, erhöht sie sich um 1 in (fast) den entsprechenden Stellen. Die cumsumFunktion (die im Grunde das Gegenteil von ist diff) kann dies tun.

> cumsum(nonpos)
 [1] 0 0 0 1 1 1 1 1 1 1 2 2 2 2

Es gibt jedoch zwei Probleme: Die Zahlen sind eins zu klein; und wir vermissen das erste Element (es sollten vier in der ersten Klasse sein).

Das erste Problem ist einfach gelöst: 1+cumsum(nonpos) . Und das zweite erfordert nur das Hinzufügen eines a 1an der Vorderseite des Vektors, da das erste Element immer in der Klasse ist 1:

 > classes <- c(1, 1 + cumsum(nonpos))
 > classes
  [1] 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3

Jetzt können wir es mit wieder an unseren Datenrahmen anhängen cbind (mithilfe der class=Syntax können wir der Spalte die classÜberschrift geben):

 > data_w_classes <- cbind(data, class=classes)

Und data_w_classesenthält jetzt das Ergebnis.

Endergebnis

Wir können die Zeilen zusammen komprimieren und alles in eine Funktion zusammenfassen, um die Verwendung zu vereinfachen:

classify <- function(data) {
   cbind(data, class=c(1, 1 + cumsum(diff(data$h_no) <= 0)))
}

Oder, da es Sinn macht für die class , ein Faktor zu sein:

classify <- function(data) {
   cbind(data, class=factor(c(1, 1 + cumsum(diff(data$h_no) <= 0))))
}

Sie verwenden eine der folgenden Funktionen:

> classified <- classify(data) # doesn't overwrite data
> data <- classify(data) # data now has the "class" column

(Diese Methode zur Lösung dieses Problems ist gut, da sie eine explizite Iteration vermeidet, die im Allgemeinen für R empfohlen wird, und das Erzeugen vieler Zwischenvektoren und Listen usw. vermeidet. Außerdem ist es ziemlich ordentlich, wie es in eine Zeile geschrieben werden kann :))

huon
quelle
2

Zusätzlich zu Romans Antwort könnte so etwas noch einfacher sein. Beachten Sie, dass ich es nicht getestet habe, da ich momentan keinen Zugriff auf R habe.

# Note that I use a global variable here
# normally not advisable, but I liked the
# use here to make the code shorter
index <<- 0
new_column = sapply(df$h_no, function(x) {
  if(x == 1) index = index + 1
  return(index)
})

Die Funktion durchläuft die Werte in n_hound gibt immer die Kategorie zurück, zu der der aktuelle Wert gehört. Wenn ein Wert von 1erkannt wird, erhöhen wir die globale Variable indexund fahren fort.

Paul Hiemstra
quelle
Ich mag den Hack mit der globalen Variablen. Also Cish. : P
Roman Luštrik
2

Ich glaube, dass die Verwendung von "cbind" der einfachste Weg ist, eine Spalte zu einem Datenrahmen in R hinzuzufügen. Unten ein Beispiel:

    myDf = data.frame(index=seq(1,10,1), Val=seq(1,10,1))
    newCol= seq(2,20,2)
    myDf = cbind(myDf,newCol)
Emanuele Catania
quelle
1
Data.frame[,'h_new_column'] <- as.integer(Data.frame[,'h_no'], breaks=c(1, 4, 7))
user2759975
quelle
0

Ansatz basierend auf der Identifizierung der Anzahl der Gruppen ( xin mapply) und ihrer Länge ( yin mapply)

mytb<-read.table(text="h_no  h_freq  h_freqsq group
1     0.09091 0.008264628 1
2     0.00000 0.000000000 1
3     0.04545 0.002065702 1
4     0.00000 0.000000000 1  
1     0.13636 0.018594050 2
2     0.00000 0.000000000 2
3     0.00000 0.000000000 2
4     0.04545 0.002065702 2
5     0.31818 0.101238512 2
6     0.00000 0.000000000 2
7     0.50000 0.250000000 2 
1     0.13636 0.018594050 3 
2     0.09091 0.008264628 3
3     0.40909 0.167354628 3
4     0.04545 0.002065702 3", header=T, stringsAsFactors=F)
mytb$group<-NULL

positionsof1s<-grep(1,mytb$h_no)

mytb$newgroup<-unlist(mapply(function(x,y) 
  rep(x,y),                      # repeat x number y times
  x= 1:length(positionsof1s),    # x is 1 to number of nth group = g1:g3
  y= c( diff(positionsof1s),     # y is number of repeats of groups g1 to penultimate (g2) = 4, 7
        nrow(mytb)-              # this line and the following gives number of repeat for last group (g3)
          (positionsof1s[length(positionsof1s )]-1 )  # number of rows - position of penultimate group (g2) 
      ) ) )
mytb
Ferroao
quelle