2016-06-21 3 views
1

Относительно новый с R для такого рода вещей, поиск довольно много и не мог найти много полезного.слияние нескольких кадров данных с повторяющимися строками в R

У меня около 150 файлов .csv с 40 000 - 60 000 строк каждый, и я пытаюсь объединить 3 столбца из каждого в один большой фрейм данных. У меня есть небольшой скрипт, который извлекает 3 столбца с интересом («id», «name» и «value») из каждого файла и объединяется с «id» и «name» с более крупным фреймом данных «MergedData». Вот мой код (я уверен, что это очень неэффективный способ сделать это, и что теперь все в порядке со мной, но, конечно, я открыт для лучших вариантов!):

file_list <- list.files() 

for (file in file_list){ 

    if(!exists("MergedData")){ 
    MergedData <- read.csv(file, skip=5)[ ,c("id", "name", "value")] 
    colnames(MergedData) <- c("id", "name", file) 
    } 

    else if(exists("MergedData")){ 
    temp_data <- read.csv(file, skip=5)[ ,c("id", "name", "value")] 
    colnames(temp_data) <- c("id", "name", file) 
    MergedData <- merge(MergedData, temp_data, by=c("id", "name"), all=TRUE) 
    rm(temp_data) 
    } 
} 

Не каждый файл имеет такое же количество строк, хотя многие строки являются общими для многих файлов. У меня нет инклюзивного списка строк, поэтому я включил all = TRUE для добавления новых строк, которые еще не существуют в файле MergedData.

Моя проблема: многие файлы содержат 2-4 строки с одинаковыми «id» и «name», но разные записи «value». Поэтому, когда я их объединяю, я добавляю строки для каждой возможной комбинации, которая быстро выходит из-под контроля. Самое разочаровывающее, что ни один из этих дубликатов не интересует меня вообще. Есть ли простой способ принять значение для первой записи и просто игнорировать любые повторяющиеся записи?

Спасибо!

ответ

0

Основываясь на ваш комментарии, мы могли бы сложить каждый файл и затем отливал результирующий кадр данных от «длинного» «широкого» формата:

library(dplyr) 
library(readr) 
library(reshape2) 

df = lapply(file_list, function(file) { 
      dat = read_csv(file) 
      dat$source.file = file 
      return(dat) 
     }) 
df = bind_rows(df) 
df = dcast(df, id + name ~ source.file, value.var="value") 

В приведенной выше коде, после прочтения в каждом файл, мы добавьте новый столбец source.file, содержащий имя файла (или его модифицированную версию). * Затем мы используем dcast для переноса фрейма данных из «длинного» в «широкий» формат для создания отдельного столбца для value из каждого файла, причем каждый новый столбец с одним из имен, которые мы только что создали в source.file.

Обратите внимание, что в зависимости от того, что вы планируете делать с этим фреймом данных, вам может быть удобнее хранить его в длинном формате (то есть пропустить шаг dcast) для дальнейшего анализа.

Addendum: Работа с Aggregation function missing: defaulting to length предупреждение. Это происходит, когда у вас есть несколько строк с одинаковыми id, name и source.file. Это означает, что есть несколько value, которые должны быть отображены в одну ячейку, что приводит к агрегации. Функция агрегации по умолчанию - length (т. Е. Счетчик количества значений в этой ячейке). Единственные способы, которыми я знаю, - (a) сохранить данные в длинном формате, (б) использовать другую функцию агрегации (например, mean) или (c) добавить дополнительный столбец counter, чтобы различать случаи с несколькими значениями для та же комбинация id, name и source.file. Мы демонстрируем это ниже.

Во-первых, давайте создадим некоторые поддельные данные:

df = data.frame(id=rep(1:2,2), 
       name=rep(c("A","B"), 2), 
       source.file=rep(c("001","002"), each=2), 
       value=11:14) 

df 
id name source.file value 
1 1 A   001 11 
2 2 B   001 12 
3 1 A   002 13 
4 2 B   002 14 
  1. только одно значение в комбинации id, name и source.file, так dcast работ по желанию.

    dcast(df, id + name ~ source.file, value.var="value") 
    
    id name 001 002 
    1 1 A 11 13 
    2 2 B 12 14 
    
  2. Добавьте дополнительный ряд с тем же id, name и source.file. Так как теперь есть две value s, которые отображаются в одну ячейку, dcast должны собираться. Функция агрегации по умолчанию - это подсчет количества значений.

    df = rbind(df, data.frame(id=1, name="A", source.file="002", value=50)) 
    
    dcast(df, id + name ~ source.file, value.var="value") 
    
    Aggregation function missing: defaulting to length 
    
        id name 001 002 
    1 1 A 1 2 
    2 2 B 1 1 
    
  3. Вместо этого следует использовать mean как функции агрегации.

    dcast(df, id + name ~ source.file, value.var="value", fun.aggregate=mean) 
    
    id name 001 002 
    1 1 A 11 31.5 
    2 2 B 12 14.0 
    
  4. Добавить новый столбец counter дифференцироваться случаи, когда Есть несколько строк с одинаковым id, name и source.file и включают в себя, что в dcast. Это возвращает нас к одному значению на ячейку, но за счет наличия более одного столбца для некоторого source.file с.

    # Add counter column 
    df = df %>% group_by(id, name, source.file) %>% 
        mutate(counter=1:n()) 
    

    Как вы можете видеть, значение counter только имеет значение 1, в тех случаях, когда есть только одна комбинация id, name и source.file, но имеет значение 1 и 2 для одного случая, когда есть два ряда с одинаковыми id, name и source.file (строки 3 и 5 ниже).

    df 
    
     id name source.file value counter 
    1  1  A   001 11  1 
    2  2  B   001 12  1 
    3  1  A   002 13  1 
    4  2  B   002 14  1 
    5  1  A   002 50  2 
    

    Теперь мы dcast с counter включены, так что мы получаем две колонки для source.file "002".

    dcast(df, id + name ~ source.file + counter, value.var="value") 
    
    id name 001_1 002_1 002_2 
    1 1 A 11 13 50 
    2 2 B 12 14 NA 
    

* Я не уверен, что ваши имена файлов похожи, так что вам, возможно, потребуется скорректировать это создать формат имен с уникальным Идентификатор файла. Например, если ваши имена файлов соответствуют шаблону «file001.csv», «file002.csv» и т. Д., Вы можете сделать это: dat$source.file = paste0("Value", gsub("file([0-9]{3})\\.csv", "\\1", file).

+0

Хмм, я не думаю, что объяснил это достаточно хорошо. Моя конечная цель - это кадр данных с столбцами «id», «name», а затем 150 столбцов для «значения» из каждого из 150 файлов. Если файл имеет значение для этого «id» «name» combo, я хочу его, если он не соответствует NA. Если я их складываю, удалите повторяющиеся записи «id» «name», не удаляю ли я связанные данные? – JRoon

+0

См. Обновленный код и дайте мне знать, если это ближе к тому, что вы искали. – eipi10

+0

Это близко. Я получаю несколько предупреждений: '> df = bind_rows (df)' Предупреждающие сообщения: 1: В rbind_all (x, .id): Неодинаковые уровни факторов: принуждение к символу 2: В rbind_all (x, .id): Неодинаковые уровни факторов: принуждение к символу '> df = dcast (df, id + name ~ source.file, value.var = "value")' Отсутствует функция агрегирования: по умолчанию длина. Результирующий блок данных структурирован правильно, но значения фактически являются подсчетами записей в каждом CSV-файле для этой строки, а не самими значениями. – JRoon