2011-11-27 12 views
18

Sto cercando di capire il modo migliore di produrre un file JSON da R. Ho il seguente dataframe tmp in R.Strategie per la formattazione dell'output JSON da 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 

L'uscita dput(tmp) è come segue:

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 il pacchetto rjson, corro la linea toJSON(tmp) che produce il seguente file 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]} 

Ho anche sperimentato con il pacchetto RJSONIO; l'output di toJSON() era lo stesso. Quello che vorrei produrre è la seguente struttura:

{"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}, 
      .... 
      ] 

Non sono sicuro di come sia meglio farlo. Mi rendo conto che posso analizzarlo riga per riga usando python ma ritengo che probabilmente c'è un modo migliore per farlo. Mi rendo anche conto che la mia struttura dati in R non riflette il meta-informazioni desiderate nel mio JSON di file (in particolare la linea traits), ma sono soprattutto interessato a produrre i dati formattati come la linea

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

come ho può aggiungere manualmente la prima riga.


EDIT: Ho trovato un utile blog post in cui l'autore affrontato un problema simile e ha fornito una soluzione. Questa funzione produce un file JSON formattato da un frame di dati.

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

anni sono andati avanti da questa domanda è stato chiesto. Posso suggerire di guardare la biblioteca (jsonlite) - qualcun altro ha fornito la risposta qui sotto. Non richiede le trasformazioni apply() e produce direttamente l'output JSON previsto. – ripvlan

+1

@ripvlan - jsonlite contrassegnato come risposta – djq

risposta

7

Usando il pacchetto 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

Ciao Alex - Mi è piaciuta questa risposta al meglio. jsonlite capisce data.frame e produce senza la trasformazione alply()/apply() come descritto nella risposta di @civilstat sopra. Inoltre, mi sono imbattuto in un problema con RJSONIO che non sono stato in grado di risolvere in modo che tutti i valori fossero array di 1. jsonlite non avesse questo problema. – ripvlan

+0

grazie per il feedback, sono felice che tu l'abbia trovato utile. – Alex

4

Mi sembra si può fare questo con l'invio di ogni riga del vostro data.frame per JSON con l'apply dichiarazione appropriata.

Per una singola riga:

library(RJSONIO) 

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

L'intero 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

Grazie a @Andrie, è un'ottima idea. Sto cercando di capire come inserire un ',' alla fine di ogni riga usando qualcosa come questo: 'x <- apply (tmp, 1, function (tmp) {paste (toJSON, ',')}) '. C'è un modo per aggiungere qualcosa a ciò che viene restituito da 'toJSON'? – djq

+0

Ho appena capito: 'x <- apply (tmp, 1, function (tmp) {paste (toJSON (tmp), ',')})'. Impara ancora ad applicare! – djq

+0

@celenius: Sembra sospettosamente che tu stia cercando di scrivere pezzi di JSON, che finiranno solo in lacrime. Meglio manipolare i dati in una forma in cui chiamare 'toJSON' ti dà quello che vuoi. Vedi la mia risposta. –

14

Basandosi sulle idea di Andrie con apply, è possibile ottenere esattamente quello che vuoi, modificando la variabile tmp prima di chiamare 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ì, d'accordo, questo è l'approccio migliore. – Andrie

+0

Un problema che ho avuto con entrambi gli approcci ('rjson' e' RJSONIO') è che convertono tutti i numeri in stringhe. Ad esempio, '2' diventa' "2" '. Lo snippet di codice che ho incollato come parte della mia domanda evita questo problema mentre controlla 'numerico' e' numero intero'. Forse dovrei aggiungerlo come risposta. – djq

+0

@celenius: La mia soluzione applicata alla variabile 'tmp' ha interi interi, non interi convertiti in stringa. Sei sicuro di non fare qualcosa di sciocco? –

9

costruzione più avanti Andrie e Richie delle idee, utilizzare alply invece di apply per evitare la conversione di numeri di caratteri:

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

di alply è simile a apply ma restituisce una lista automaticamente plyr; mentre senza la funzione più complicata all'interno della risposta di Richie Cotton, apply restituirebbe un vettore o un array. E questi passaggi aggiuntivi, tra cui t, indicano che se il set di dati contiene colonne non numeriche, i numeri verranno convertiti in stringhe. Quindi l'uso di alply evita questo problema.

Per esempio, prendete il vostro tmp set di dati e aggiungere

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

quindi confrontare questo codice (con alply) contro l'altro esempio (con apply).

2

Un'altra opzione consiste nell'utilizzare lo split per dividere il numero data.frame con N righe in N.frammi con 1 riga.

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