mtcars - Agregar una columna a un data.frame
r data frame mtcars (5)
Tengo el data.frame a continuación. Quiero agregar una columna que clasifique mis datos de acuerdo con la columna 1 ( h_no
) de esa manera que la primera serie de h_no 1,2,3,4 sea de clase 1, la segunda serie de h_no
(de 1 a 7) sea de clase 2 etc. como se indica en la última columna.
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
Además de la respuesta de Roman, algo como esto podría ser incluso más simple. Tenga en cuenta que no lo he probado porque no tengo acceso a R en este momento.
# 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)
})
La función itera sobre los valores en n_ho
y siempre devuelve la categoría a la que pertenece el valor actual. Si se detecta un valor de 1
, aumentamos el index
variable global y continuamos.
Fácilmente: su marco de datos es A
b <- A[,1]
b <- b==1
b <- cumsum(b)
Luego obtienes la columna b.
Puede agregar una columna a sus datos usando varias técnicas. Las citas a continuación provienen de la sección "Detalles" del texto de ayuda relevante, [[.data.frame
.
Los marcos de datos se pueden indexar en varios modos. Cuando
[
y[[
se usan con un índice vectorial único (x[i]
ox[[i]]
), indexan el marco de datos como si fuera una lista.
my.dataframe["new.col"] <- a.vector
my.dataframe[["new.col"]] <- a.vector
El método data.frame para
$
, trata ax
como una lista
my.dataframe$new.col <- a.vector
Cuando
[
y[[
se usan con dos índices (x[i, j]
x[[i, j]]
) actúan como indexar una matriz
my.dataframe[ , "new.col"] <- a.vector
Dado que el método para data.frame
asume que si no especifica si está trabajando con columnas o filas, asumirá que quiere decir columnas.
Para su ejemplo, esto debería funcionar:
# 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
Si entiendo la pregunta correctamente, quiere detectar cuándo h_no
no aumenta y luego incrementar la class
. (Voy a ver cómo resolví este problema, al final hay una función autónoma).
Trabajando
Solo nos importa la columna h_no
por el momento, así que podemos extraer eso del marco de datos:
> h_no <- data$h_no
Queremos detectar cuándo h_no
no sube, lo que podemos hacer trabajando cuando la diferencia entre los elementos sucesivos es negativa o cero. R proporciona la función diff
que nos da el vector de diferencias:
> d.h_no <- diff(h_no)
> d.h_no
[1] 1 1 1 -3 1 1 1 1 1 1 -6 1 1 1
Una vez que tenemos eso, es simple encontrar los que no son positivos:
> nonpos <- d.h_no <= 0
> nonpos
[1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
[13] FALSE FALSE
En R, TRUE
y FALSE
son básicamente lo mismo que 1
y 0
, de modo que si obtenemos la suma acumulativa de nonpos
, aumentará en 1 en (casi) los puntos apropiados. La función cumsum
(que es básicamente lo opuesto a diff
) puede hacer esto.
> cumsum(nonpos)
[1] 0 0 0 1 1 1 1 1 1 1 2 2 2 2
Pero, hay dos problemas: los números son uno demasiado pequeño; y, nos falta el primer elemento (debe haber cuatro en la primera clase).
El primer problema simplemente se resuelve: 1+cumsum(nonpos)
. Y el segundo solo requiere agregar un 1
al frente del vector, ya que el primer elemento siempre está en la clase 1
:
> classes <- c(1, 1 + cumsum(nonpos))
> classes
[1] 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3
Ahora, podemos adjuntarlo a nuestro marco de datos con cbind
(usando la sintaxis class=
, podemos darle a la columna el encabezado de la class
):
> data_w_classes <- cbind(data, class=classes)
Y data_w_classes
ahora contiene el resultado.
Resultado final
Podemos comprimir las líneas juntas y envolver todo en una función para que sea más fácil de usar:
classify <- function(data) {
cbind(data, class=c(1, 1 + cumsum(diff(data$h_no) <= 0)))
}
O bien, dado que tiene sentido que la class
sea un factor:
classify <- function(data) {
cbind(data, class=factor(c(1, 1 + cumsum(diff(data$h_no) <= 0))))
}
Usas cualquiera de las funciones como:
> classified <- classify(data) # doesn''t overwrite data
> data <- classify(data) # data now has the "class" column
(Este método para resolver este problema es bueno porque evita la iteración explícita, que generalmente se recomienda para R, y evita generar muchos vectores intermedios y listas, etc. Y también es un poco nítido cómo se puede escribir en una línea :))
Data.frame[,''h_new_column''] <- as.integer(Data.frame[,''h_no''], breaks=c(1, 4, 7))