2011-11-27 12 views
18

Estoy tratando de encontrar la mejor manera de producir un archivo JSON desde R. Tengo el siguiente dataframe tmp en R.Estrategias para formatear la salida JSON desde R

> tmp 
    gender age welcoming proud tidy unique 
1  1 30   4  4 4  4 
2  2 34   4  2 4  4 
3  1 34   5  3 4  5 
4  2 33   2  3 2  4 
5  2 28   4  3 4  4 
6  2 26   3  2 4  3 

La salida del dput(tmp) es como sigue:

tmp <- structure(list(gender = c(1L, 2L, 1L, 2L, 2L, 2L), age = c(30, 
34, 34, 33, 28, 26), welcoming = c(4L, 4L, 5L, 2L, 4L, 3L), proud = c(4L, 
2L, 3L, 3L, 3L, 2L), tidy = c(4L, 4L, 4L, 2L, 4L, 4L), unique = c(4L, 
4L, 5L, 4L, 4L, 3L)), .Names = c("gender", "age", "welcoming", 
"proud", "tidy", "unique"), na.action = structure(c(15L, 39L, 
60L, 77L, 88L, 128L, 132L, 172L, 272L, 304L, 305L, 317L, 328L, 
409L, 447L, 512L, 527L, 605L, 618L, 657L, 665L, 670L, 708L, 709L, 
729L, 746L, 795L, 803L, 826L, 855L, 898L, 911L, 957L, 967L, 983L, 
984L, 988L, 1006L, 1161L, 1162L, 1224L, 1245L, 1256L, 1257L, 
1307L, 1374L, 1379L, 1386L, 1387L, 1394L, 1401L, 1408L, 1434L, 
1446L, 1509L, 1556L, 1650L, 1717L, 1760L, 1782L, 1814L, 1847L, 
1863L, 1909L, 1930L, 1971L, 2004L, 2022L, 2055L, 2060L, 2065L, 
2082L, 2109L, 2121L, 2145L, 2158L, 2159L, 2226L, 2227L, 2281L 
), .Names = c("15", "39", "60", "77", "88", "128", "132", "172", 
"272", "304", "305", "317", "328", "409", "447", "512", "527", 
"605", "618", "657", "665", "670", "708", "709", "729", "746", 
"795", "803", "826", "855", "898", "911", "957", "967", "983", 
"984", "988", "1006", "1161", "1162", "1224", "1245", "1256", 
"1257", "1307", "1374", "1379", "1386", "1387", "1394", "1401", 
"1408", "1434", "1446", "1509", "1556", "1650", "1717", "1760", 
"1782", "1814", "1847", "1863", "1909", "1930", "1971", "2004", 
"2022", "2055", "2060", "2065", "2082", "2109", "2121", "2145", 
"2158", "2159", "2226", "2227", "2281"), class = "omit"), row.names = c(NA, 
6L), class = "data.frame") 

Usando el paquete rjson, corro la línea de toJSON(tmp) que produce el siguiente archivo JSON:

{"gender":[1,2,1,2,2,2], 
"age":[30,34,34,33,28,26], 
"welcoming":[4,4,5,2,4,3], 
"proud":[4,2,3,3,3,2], 
    "tidy":[4,4,4,2,4,4], 
    "unique":[4,4,5,4,4,3]} 

también he experimentado con el paquete RJSONIO; la salida de toJSON() fue la misma. Lo que me gustaría producir es la siguiente estructura:

{"traits":["gender","age","welcoming","proud", "tidy", "unique"], 
    "values":[ 
      {"gender":1,"age":30,"welcoming":4,"proud":4,"tidy":4, "unique":4}, 
      {"gender":2,"age":34,"welcoming":4,"proud":2,"tidy":4, "unique":4}, 
      .... 
      ] 

No estoy seguro de cómo hacer esto. Me doy cuenta de que puedo analizarlo línea por línea usando python, pero creo que hay una forma mejor de hacerlo. También me di cuenta de que mi estructura de datos en R no refleja la meta-información deseada en mi JSON archivo (en concreto la línea traits), pero estoy interesado principalmente en la producción de los datos formateados como la línea

{"gender":1,"age":30,"welcoming":4,"proud":4,"tidy":4, "unique":4} 

como yo puede agregar manualmente la primera línea.


EDIT: me encontré con un útil blog post donde el autor aborda un problema similar y proporcionó una solución. Esta función produce un archivo JSON formateado a partir de un marco de datos.

toJSONarray <- function(dtf){ 
clnms <- colnames(dtf) 

name.value <- function(i){ 
quote <- ''; 
# if(class(dtf[, i])!='numeric'){ 
if(class(dtf[, i])!='numeric' && class(dtf[, i])!= 'integer'){ # I modified this line so integers are also not enclosed in quotes 
quote <- '"'; 
} 

paste('"', i, '" : ', quote, dtf[,i], quote, sep='') 
} 

objs <- apply(sapply(clnms, name.value), 1, function(x){paste(x, collapse=', ')}) 
objs <- paste('{', objs, '}') 

# res <- paste('[', paste(objs, collapse=', '), ']') 
res <- paste('[', paste(objs, collapse=',\n'), ']') # added newline for formatting output 

return(res) 
} 
+0

años han pasado desde que se hizo esta pregunta. Puedo sugerir mirar la biblioteca (jsonlite) - alguien más proporcionó la respuesta a continuación. No requiere las transformaciones de apply() y produce directamente la salida JSON esperada. – ripvlan

+1

@ripvlan - marcado jsonlite como respuesta – djq

Respuesta

7

Usando el paquete jsonlite:

> jsonlite::toJSON(list(traits = names(tmp), values = tmp), pretty = TRUE) 
{ 
    "traits": ["gender", "age", "welcoming", "proud", "tidy", "unique"], 
    "values": [ 
    { 
     "gender": 1, 
     "age": 30, 
     "welcoming": 4, 
     "proud": 4, 
     "tidy": 4, 
     "unique": 4 
    }, 
    { 
     "gender": 2, 
     "age": 34, 
     "welcoming": 4, 
     "proud": 2, 
     "tidy": 4, 
     "unique": 4 
    }, 
    { 
     "gender": 1, 
     "age": 34, 
     "welcoming": 5, 
     "proud": 3, 
     "tidy": 4, 
     "unique": 5 
    }, 
    { 
     "gender": 2, 
     "age": 33, 
     "welcoming": 2, 
     "proud": 3, 
     "tidy": 2, 
     "unique": 4 
    }, 
    { 
     "gender": 2, 
     "age": 28, 
     "welcoming": 4, 
     "proud": 3, 
     "tidy": 4, 
     "unique": 4 
    }, 
    { 
     "gender": 2, 
     "age": 26, 
     "welcoming": 3, 
     "proud": 2, 
     "tidy": 4, 
     "unique": 3 
    } 
    ] 
} 
+1

Hola Alex - Me gustó esta respuesta lo mejor. jsonlite entiende data.frame y outputs sin la transformación alply()/apply() como se describe en la respuesta @civilstat anterior. Además, me encontré con un problema con RJSONIO que no he podido resolver y que hace que todos los valores sean matrices de 1. jsonlite no tenía este problema. – ripvlan

+0

gracias por los comentarios, me alegra que lo haya encontrado útil. – Alex

4

Me parece que puede hacer esto enviando cada fila de su data.frame a JSON con el apply declaración apropiada.

Para una sola fila:

library(RJSONIO) 

> x <- toJSON(tmp[1, ]) 
> cat(x) 
{ 
"gender": 1, 
"age":  30, 
"welcoming": 4, 
"proud": 4, 
"tidy": 4, 
"unique": 4 
} 

La totalidad data.frame:

x <- apply(tmp, 1, toJSON) 
cat(x) 
{ 
"gender": 1, 
"age":  30, 
"welcoming": 4, 
"proud": 4, 
"tidy": 4, 
"unique": 4 
} { 

... 

} { 
"gender": 2, 
"age":  26, 
"welcoming": 3, 
"proud": 2, 
"tidy": 4, 
"unique": 3 
} 
+0

Gracias @Andrie, esa es una gran idea. Estoy intentando descubrir cómo insertar un ',' al final de cada línea usando algo como esto: 'x <- apply (tmp, 1, function (tmp) {paste (toJSON, ',')}) '. ¿Hay alguna manera de agregar algo a lo que se devuelve desde 'toJSON'? – djq

+0

Acabo de descifrarlo: 'x <- apply (tmp, 1, function (tmp) {paste (toJSON (tmp), ',')})'. ¡Todavía estoy aprendiendo a aplicar! – djq

+0

@celenius: Parece sospechosamente que estás tratando de escribir fragmentos de JSON, que solo terminarán en lágrimas. Es mejor manipular los datos en un formato en el que llamar a 'toJSON' le proporciona lo que desea. Ver mi respuesta –

14

Sobre la base de la idea de Andrie con apply, puede obtener exactamente lo que quiere mediante la modificación de la variable tmp antes de llamar toJSON.

library(RJSONIO) 
modified <- list(
    traits = colnames(tmp), 
    values = unname(apply(tmp, 1, function(x) as.data.frame(t(x)))) 
) 
cat(toJSON(modified)) 
+0

+1 Sí, acordó que este es el mejor enfoque. – Andrie

+0

Un problema que he tenido con ambos enfoques ('rjson' y' RJSONIO') es que convierten todos los números en cadenas. Por ejemplo '2' se convierte en' "2" '. El fragmento de código que pegué como parte de mi pregunta evita este problema, ya que comprueba "numérico" y "entero". Quizás debería agregarlo como una respuesta. – djq

+0

@celenius: Mi solución aplicada a su variable 'tmp' tiene enteros enteros, no enteros convertidos a cadenas. ¿Estás seguro de que no estás haciendo algo tonto? –

9

edificio aún más en las ideas Andrie y Richie, utilice alply en lugar de apply para evitar la conversión de números a caracteres:

library(RJSONIO) 
library(plyr) 
modified <- list(
    traits = colnames(tmp), 
    values = unname(alply(tmp, 1, identity)) 
) 
cat(toJSON(modified)) 

de alply es similar a apply pero devuelve una lista de forma automática plyr; mientras que sin la función más complicada dentro de la respuesta de Richie Cotton, apply devolvería un vector o matriz. Y esos pasos adicionales, incluido t, significan que si su conjunto de datos tiene columnas no numéricas, los números se convertirán en cadenas. Así que el uso de alply evita esa preocupación.

Por ejemplo, tome su tmp conjunto de datos y añadir

tmp$grade <- c("A","B","C","D","E","F") 

luego comparar este código (con alply) vs el otro ejemplo (con apply).

2

Otra opción es usar el split para dividir su data.frame con N filas en N data.frames con 1 fila.

library(RJSONIO) 
modified <- list(
    traits = colnames(tmp), 
    values = split(tmp, seq_len(nrow(tmp))) 
) 
cat(toJSON(modified)) 
Cuestiones relacionadas