Эффективная альтернатива merge () при построении фрейма данных из файлов JSON с R? - PullRequest
5 голосов
/ 03 марта 2011

Я написал следующий код, который работает, но мучительно медленно, как только я запускаю его для тысяч записей:

require("RJSONIO")
people_data <- data.frame(person_id=numeric(0))

json_data <- fromJSON(json_file)
n_people <- length(json_data)
for(person in 1:n_people) {
        person_dataframe <- as.data.frame(t(unlist(json_data[[person]])))
        people_data <- merge(people_data, person_dataframe, all=TRUE)
    }

output_file <- paste("people_data",".csv")
write.csv(people_data, file=output_file)

Я пытаюсь создать единую таблицу данных из серии json-форматированных файлов. Функция fromJSON() считывает данные как списки списков. Каждый элемент списка - это человек, который затем содержит список атрибутов для этого человека.

Например:

[[1]]
    person_id
    name
    gender
    hair_color
[[2]]
    person_id
    name
    location
    gender
    height

[[...]]

structure(list(person_id = "Amy123", name = "Amy", gender = "F",
               hair_color = "brown"), 
          .Names = c("person_id", "name", "gender", "hair_color"))

structure(list(person_id = "matt53", name = "Matt", 
               location = structure(c(47231, "IN"), 
                                    .Names = c("zip_code", "state")), 
               gender = "M", height = 172), 
          .Names = c("person_id", "name", "location", "gender", "height"))

Конечным результатом приведенного выше кода является матрица, где столбцы - это каждый атрибут человека, который появляется в приведенной выше структуре, а строки - соответствующие значения для каждого человека. Однако, как вы можете видеть, некоторые данные отсутствуют для некоторых людей, поэтому мне нужно убедиться, что они отображаются как NA, и убедиться, что все заканчивается в правильных столбцах. Кроме того, location сам по себе является вектором с двумя компонентами: state и zip_code, что означает, что он должен быть сведен к location.state и location.zip_code, прежде чем он может быть объединен с записью другого человека; это то, что я использую unlist() для. Затем я сохраняю таблицу рабочего мастера в people_data.

Приведенный выше код работает, но знаете ли вы о более эффективном способе выполнить то, что я пытаюсь сделать? Похоже, merge() замедляет это для сканирования ... У меня есть сотни файлов с сотнями людей в каждом файле.

Спасибо! Bryan

UPDATE: Основываясь на отзывах, приведенных ниже, я попытался составить список всех людей, а затем преобразовать все это за один раз в массив данных. Я позволил этому работать быстро и все еще не закончил создание кадра данных. В списке около 1/2 миллиона человек. Эти коды выглядят так:

require("RJSONIO")
require("plyr")
people_data <- data.frame(person_id=numeric(0))
people_list <- list()

json_data <- fromJSON(json_file)
n_people <- length(json_data)
for(person in 1:n_people) {
        people_list[[person]] <- t(unlist(json_data[[person]]))
    }

#PROBLEM CODE, SLOW, 1/2 million records in people_list
people_data <- rbind.fill(lapply(people_list, as.data.frame))

output_file <- paste("people_data",".csv")
write.csv(people_data, file=output_file)

Ответы [ 3 ]

3 голосов
/ 03 марта 2011

Если вы не ожидаете, что дублирующиеся записи существуют, вы можете использовать rbind.fill из пакета plyr.

1 голос
/ 05 марта 2011

Я предполагаю, что вы знаете все имена полей.Вот решение.

Пример данных

data_list <- list (list (person_id = "Amy123", name = "Amy", sex = "F", hair_color = "brown)"), список (person_id =" matt53 ", name =" Matt ", location = list (c (" zip_code "= 47231," state "=" IN ")), пол =" M ", высота = 172))</p>

Имена для примеров записей

nm1 <- names (unlist (data_list [[1]]))) nm2 <- names (unlist (data_list [[2]]))) nm <- c (nm2, nm1 [! nm1% в% nm2]) </p>

Код программы

record_template <- vector(mode="character",length=length(nm))
names(record_template) <- nm
record_template <- "NA"
rec <- function(x) {
              tmp <- record_template
              tmp[names(unlist(x))] <- unlist(x)
              return(tmp)
       }

do.call(rbind,lapply(data_list,rec))

РЕДАКТИРОВАТЬ

Вы можете получитьимена из списка с использованием:

nm <- unique(unlist(lapply(data_list,function(x) names(unlist(x))),use.names=F))
0 голосов
/ 20 апреля 2012

Это должен быть комментарий, но ldply может быть более эффективным, как только вы вернете свои объекты в списке.

Например, когда вы сделаете это:

for(person in 1:n_people) {
    people_list[[person]] <- t(unlist(json_data[[person]]))
}

вы можете просто использовать ldply так:

people_data <- ldply(people_list,function(x) as.data.frame)

Теперь это может быть не намного быстрее, но вы можете эффективно распараллелить это так:

people_data <- ldply(people_list,function(x) as.data.frame,.parallel=TRUE)

Что может дать вам больше прироста скорости

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...