2015-12-08 4 views
4

J'ai une table de données où l'une des colonnes contient JSON. J'essaie d'extraire le contenu pour que chaque variable soit une colonne.R convertir json pour lister data.table

library(jsonlite) 
library(data.table) 

df<-data.table(a=c('{"tag_id":"34","response_id":2}', 
        '{"tag_id":"4","response_id":1,"other":4}', 
        '{"tag_id":"34"}'),stringsAsFactors=F) 

Le résultat souhaité, qui ne se réfère pas à la "autre" variable:

tag_id response_id 
1  "34"  2 
2  "4"  1 
3  "34"  NA 

J'ai essayé plusieurs versions de:

parseLog <- function(x){ 
    if (is.na(x)) 
    e=c(tag_id=NA,response_id=NA) 
    else{ 
    j=fromJSON(x) 
    e=c(tag_id=as.integer(j$tag_id),response_id=j$response_id) 
    } 
    e 
} 

qui semble bien fonctionner pour récupérer une liste de vecteurs (ou des listes si c est remplacé par list) mais quand j'essaye de convertir la liste en data.table, quelque chose ne fonctionne pas comme prévu.

parsed<-lapply(df$a,parseLog) 
    rparsed<-do.call(rbind.data.frame,parsed) 
    colnames(rparsed)<-c("tag_id","response_id") 

En raison de la valeur manquante dans la troisième rangée. Comment puis-je le résoudre d'une manière propre? Comment puis-je faire que ma méthode parse retourne un NA pour la valeur manquante. Alternative, Y at-il un paramètre "fill" comme pour rbind qui peut être utilisé dans rbind.data.frame ou une méthode analogue?

L'ensemble de données que j'utilise a des lignes de 11M, donc la performance est importante.

De plus, il existe une méthode équivalente à rbind.data.frame pour obtenir une table de données. Comment cela serait-il utilisé? Quand je vérifie la documentation, il me renvoie à rbindlist mais il se plaint le paramètre est utilisé et si appel directement (sans do.call il se plaint du type de analysable):

rparsed<-do.call(rbindlist,fill=T,parsed) 

EDIT: Le cas que je dois couverture est plus générale, dans un ensemble de 11M enregistre toutes les circonstances possibles se produisent:

df<-data.table(a=c('{"tag_id":"34","response_id":2}', 
        '{"trash":"34","useless":2}',       
        '{"tag_id":"4","response_id":1,"other":4}', 
        NA, 
        '{"response_id":"34"}', 
        '{"tag_id":"34"}'),stringsAsFactors=F) 

et la sortie ne doit contenir que des colonnes tag_id et rESPONSE_ID.

Répondre

3

Il pourrait y avoir un moyen plus simple mais cela semble fonctionner:

library(data.table) 
library(jsonlite) 
df[, json := sapply(a, fromJSON)][, rbindlist(lapply(json, data.frame), fill=TRUE)] 

#or if you need all the columns : 
#df[, json := sapply(a, fromJSON)][, 
# c('tag_id', 'response_id') := rbindlist(lapply(json, data.frame), fill=TRUE)] 

Sortie:

> df[, json := sapply(a, fromJSON)][, rbindlist(lapply(json, data.frame), fill=TRUE)] 
    tag_id response_id 
1:  34   2 
2:  4   1 
3:  34   NA 

EDIT:

Cette solution vient après l'édition de la question supplémentaire demandes

Il y a beaucoup de façons de le faire, mais je trouve la plus simple est à la création du data.frame comme ceci:

df[, json := sapply(a, fromJSON)][, 
    rbindlist(lapply(json, function(x) data.frame(x)[-3]), fill=TRUE)] 

# tag_id response_id 
#1:  34   2 
#2:  4   1 
#3:  34   NA 
+0

J'ai édité mon exemple un peu comme je vois que, dans le but de résumant je n'ai pas inclus certaines données pertinentes. J'ai des variables supplémentaires dans le json qui ne m'intéresse pas ... beaucoup d'entre eux. Excellente réponse cependant! – Picarus

+0

Mise à jour de la réponse. – LyzandeR

+0

Néanmoins, la solution ne considère pas que le nombre de variables à ignorer est élevé, ce qui signifie que la trame de données qui peut être générée peut être énorme. Y a-t-il un moyen d'éviter cela? Peut-être ajouter les colonnes qui sont pertinentes un par un? L'ordre des variables est-il garanti dans la colonne json? Même si toute la colonne "json" que vous utilisez comme étape intermédiaire nécessitera beaucoup de mémoire. Je sais que je suis difficile, essayant juste de couvrir tous les problèmes possibles. – Picarus