presentacion - tabla contenido arcgis
Concatenar filas de un marco de datos (4)
Me gustaría tomar un marco de datos con caracteres y números, y concatenar todos los elementos de cada fila en una sola cadena, que se almacenaría como un elemento único en un vector. Como ejemplo, hago un marco de datos de letras y números, y luego me gustaría concatenar la primera fila a través de la función de pegar, y con suerte devolver el valor "A1"
df <- data.frame(letters = LETTERS[1:5], numbers = 1:5)
df
## letters numbers
## 1 A 1
## 2 B 2
## 3 C 3
## 4 D 4
## 5 E 5
paste(df[1,], sep =".")
## [1] "1" "1"
Así que pegar es convertir cada elemento de la fila en un número entero que corresponde al ''índice del nivel correspondiente'' como si fuera un factor, y lo mantiene como un vector de longitud dos. (Sé / creo que los factores que son forzados a ser personajes se comportan de esta manera, pero como R no almacena df [1,] como factor en absoluto (probado por is.factor (), no puedo verificar que es en realidad un índice para un nivel)
is.factor(df[1,])
## [1] FALSE
is.vector(df[1,])
## [1] FALSE
Entonces, si no es un vector, tiene sentido que se comporte de manera extraña, pero no puedo forzarlo a convertirlo en un vector
> is.vector(as.vector(df[1,]))
[1] FALSE
Usar as.character
no pareció ayudar en mis intentos
¿Alguien puede explicar este comportamiento?
Esto de hecho es un poco extraño, pero esto es también lo que se supone que debe suceder. Cuando crea el data.frame
como lo hizo, las letters
columna se almacenan como factor
. Naturalmente, los factores no tienen orden, por lo tanto, cuando se aplica as.numeric()
a un factor, devuelve el orden del factor. Por ejemplo:
> df[, 1]
[1] A B C D E
Levels: A B C D E
> as.numeric(df[, 1])
[1] 1 2 3 4 5
A
es el primer nivel del factor df[, 1]
por lo tanto, A
se convierte en el valor 1
, cuando se aplica as.numeric
. Esto es lo que sucede cuando llamas a paste(df[1, ])
. Como las columnas 1 y 2 son de clase diferente, pegar primero transforma ambos elementos de la fila 1 en numéricos y luego en caracteres.
Cuando desee concatenar ambas columnas, primero debe transformar la primera fila en carácter:
df[, 1] <- as.character(df[, 1])
paste(df[1,], collapse = "")
Como señaló @ sebastian-c, también puede usar stringsAsFactors = FALSE
en la creación de data.frame, luego puede omitir el paso as.character()
.
Mientras que otros se han centrado en por qué su código no funciona y cómo mejorarlo, voy a tratar de centrarme más en obtener el resultado que desea. Según su descripción, parece que puede lograr lo que quiere con pasta:
df <- data.frame(letters = LETTERS[1:5], numbers = 1:5, stringsAsFactors=FALSE)
paste(df$letters, df$numbers, sep=""))
## [1] "A1" "B2" "C3" "D4" "E5"
Puede cambiar df$letters
al carácter usando df$letters <- as.character(df$letters)
si no desea usar el argumento stringsAsFactors
.
Pero supongamos que no es lo que quieres. Supongamos que tiene cientos de columnas y desea pegarlas todas juntas. Podemos hacer eso con tu ejemplo mínimo también:
df_args <- c(df, sep="")
do.call(paste, df_args)
## [1] "A1" "B2" "C3" "D4" "E5"
EDIT: método alternativo y explicación:
Me di cuenta de que el problema que estás teniendo es una combinación del hecho de que estás usando un factor y de que estás usando el argumento sep
en lugar de collapse
(como recogió @adibender). La diferencia es que sep
da el separador entre dos vectores separados y el collapse
proporciona separadores dentro de un vector. Cuando usa df[1,]
, proporciona un único vector para paste
y, por lo tanto, debe usar el argumento de collapse
. Usando su idea de obtener cada fila y concatenarlas, la siguiente línea de código hará exactamente lo que quiera:
apply(df, 1, paste, collapse="")
Ok, ahora para las explicaciones:
¿Por qué no funciona la lista?
as.list
convierte un objeto en una lista. Entonces funciona Convertirá su dataframe en una lista y posteriormente ignorará el argumento sep=""
. c
combina objetos juntos. Técnicamente, un marco de datos es solo una lista donde cada columna es un elemento y todos los elementos tienen que tener la misma longitud. Entonces, cuando lo combino con sep=""
, simplemente se convierte en una lista regular con las columnas del marco de datos como elementos.
¿Por qué usar do.call
?
do.call
permite llamar a una función usando una lista nombrada como sus argumentos. No se puede simplemente poner la lista directamente en paste
, porque no le gustan los marcos de datos. Está diseñado para concatenar vectores. Así que recuerda que dfargs
es una lista que contiene un vector de letras, un vector de números y sep que es un vector de longitud 1 que contiene solo "". Cuando uso do.call
, la función de pegar resultante es esencialmente paste(letters, numbers, sep)
.
Pero, ¿y si mi dataframe original tuviera columnas "letters", "numbers", "squigs", "blargs"
después de lo cual agregué el separador como lo hice antes? Entonces la función pegar a través de do.call
se vería así:
paste(letters, numbers, squigs, blargs, sep)
Entonces verá que funciona para cualquier cantidad de columnas.
Para aquellos que usan la biblioteca (tidyverse), simplemente puede usar la función unir.
new.df<-df%>%
unite(together, letters, numbers, sep="")
Esto le dará una nueva columna llamada "junto" con A1, B2, etc.
si quieres comenzar con
df <- data.frame(letters = LETTERS[1:5], numbers = 1:5, stringsAsFactors=TRUE)
.. luego no hay una regla general sobre cómo las df$letters
serán interpretadas por cualquier función dada. Es un factor para las funciones de modelado, el carácter para algunos y el entero para algunos otros. Incluso la misma función, como pegar, puede interpretarlo de manera diferente, dependiendo de cómo la use:
paste(df[1,], collapse="") # "11"
apply(df, 1, paste, collapse="") # "A1" "B2" "C3" "D4" "E5"
No tiene lógica, excepto que probablemente tendrá sentido una vez que conozca las funciones internas de cada función.
Los factores parecen convertirse en enteros cuando un argumento se convierte en vector (como usted sabe, los marcos de datos son listas de vectores de igual longitud, por lo que la primera fila de un marco de datos también es una lista, y cuando se lo obliga a estar un vector, algo así sucede :)
df[1,]
# letters numbers
# 1 A 1
unlist(df[1,])
# letters numbers
# 1 1
No sé cómo apply
logra lo que hace (es decir, los factores están representados por los valores de los caracteres) - si estás interesado, mira su código fuente. Sin embargo, puede ser útil saber que puedes confiar (en este sentido específico) en apply
(en esta ocasión específica). De manera más general, es útil almacenar cada dato en un formato sensible, que incluye almacenar cadenas como cadenas, es decir, usar stringsAsFactors=FALSE
.
Por cierto, cada libro introductorio de R debería tener esta idea en un subtítulo. Por ejemplo, mi plan de jubilación es escribir "A (no tan) introducción suave al zen de la pesca de datos con R, the stringsAsFactors = FALSE way".