Обрізання величезного (3,5 ГБ) CSV-файлу для читання в R


87

Отже, у мене є файл даних (з крапкою з комою), який містить багато деталей і неповних рядків (що приводить Access і SQL до задухи). Це набір даних рівня округу, розбитий на сегменти, підсегменти та підсегменти (загалом ~ 200 факторів) протягом 40 років. Коротше кажучи, він величезний, і він не впишеться в пам’ять, якщо я спробую його просто прочитати.

Отже, моє запитання полягає в тому, що, враховуючи те, що я хочу всі округи, але лише один рік (і лише найвищий рівень сегменту ... що в кінцевому підсумку призведе до приблизно 100 000 рядків), який би був найкращий спосіб отримати це зведення в R?

В даний час я намагаюся вирізати нерелевантні роки за допомогою Python, обходячи обмеження розміру файлів, читаючи та працюючи по одному рядку за раз, але я б віддав перевагу рішенням лише для R (пакети CRAN OK). Чи існує подібний спосіб читати у файлах фрагменти за раз у R?

Будь-які ідеї будуть дуже вдячні.

Оновлення:

  • Обмеження
    • Потрібно використовувати мою машину, тому жодних екземплярів EC2
    • Як тільки R, наскільки це можливо. Швидкість і ресурси в цьому випадку не турбуються ... за умови, що моя машина не вибухне ...
    • Як ви можете бачити нижче, дані містять змішані типи, які мені потрібно оперувати пізніше
  • Дані
    • Дані складають 3,5 ГБ, приблизно 8,5 мільйонів рядків і 17 стовпців
    • Кілька тисяч рядків (~ 2k) мають неправильний вигляд, лише один стовпець замість 17
      • Вони абсолютно не важливі і можуть бути скасовані
    • Мені потрібно лише ~ 100 000 рядків з цього файлу (див. Нижче)

Приклад даних:

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP; ...
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1; ...
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5; ...
NC  [Malformed row]
[8.5 Mill rows]

Я хочу вирізати кілька стовпців і вибрати два із 40 доступних років (2009-2010 з 1980-2020), щоб дані могли вписуватися в R:

County; State; Year; Quarter; Segment; GDP; ...
Ada County;NC;2009;4;FIRE;80.1; ...
Ada County;NC;2010;1;FIRE;82.5; ...
[~200,000 rows]

Результати:

Поміркувавши з усіма запропонованими пропозиціями, я вирішив, що readLines, запропоновані JD та Marek, будуть працювати найкраще. Я дав Мареку чек, тому що він дав зразок реалізації.

Для остаточної відповіді я відтворив трохи адаптовану версію реалізації Марека, використовуючи strsplit та cat, щоб зберігати лише ті стовпці, які я хочу.

Слід також зазначити, що це НАБАГАТО менш ефективно, ніж Python ... як, наприклад, Python перебирає файл розміром 3,5 ГБ за 5 хвилин, тоді як R займає близько 60 ... але якщо у вас все є R, то це квиток.

## Open a connection separately to hold the cursor position
file.in <- file('bad_data.txt', 'rt')
file.out <- file('chopped_data.txt', 'wt')
line <- readLines(file.in, n=1)
line.split <- strsplit(line, ';')
# Stitching together only the columns we want
cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
## Use a loop to read in the rest of the lines
line <- readLines(file.in, n=1)
while (length(line)) {
  line.split <- strsplit(line, ';')
  if (length(line.split[[1]]) > 1) {
    if (line.split[[1]][3] == '2009') {
        cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
    }
  }
  line<- readLines(file.in, n=1)
}
close(file.in)
close(file.out)

Невдачі за підходом:

  • sqldf
    • Це безумовно те, що я буду використовувати для такого типу проблем у майбутньому, якщо дані будуть сформовані належним чином. Однак якщо це не так, тоді SQLite задихається.
  • MapReduce
    • Чесно кажучи, документи трохи мене залякали, тому я не зміг спробувати. Здавалося, це вимагало, щоб об'єкт також знаходився в пам'яті, що перемогло б справу, якби це було так.
  • bigmemory
    • Цей підхід чітко пов'язаний з даними, але одночасно може обробляти лише один тип. В результаті всі мої вектори персонажів падали, коли їх розміщували у великій таблиці. Якщо мені потрібно розробити великі набори даних на майбутнє, я б розглянув використання цифр лише для того, щоб зберегти цю опцію.
  • сканування
    • Здавалося, сканування має подібні проблеми із типом великої пам’яті, але з усіма механіками readLines. Коротше кажучи, цього разу це просто не відповідало законопроекту.

3
Якщо ваші критерії досить прості, ви, мабуть, можете уникнути, використовуючи sedта / або awkстворюючи скорочену версію CSV, яку ви можете прочитати безпосередньо. Оскільки це більше обхідний шлях, ніж відповідь, я залишу це як коментар.
Hank Gay

Я погоджуюся з Хенком - вам слід використовувати відповідний інструмент для роботи, і якщо це просте очищення даних / видалення нерелевантних рядків / стовпців, інструменти потокового командного рядка, такі як sort / sed / awk, є чудовими і будуть набагато менш ресурсомісткими, ніж R або python - якщо ви дасте зразок формату ваших файлів, ми могли б навести приклад
Aaron Statham

Чудово. Повідомте нас, що ви виявите.
Шейн,

@Hank & Aaron: Я, як правило, все за те, щоб використовувати правильний інструмент для роботи, але враховуючи, що це на машині Windows на роботі, і я вивчаю R, ходячи, я вважав, що це було б гарною вправою, щоб відмовитись від найкращих практик і спробуйте це, якщо можливо, лише R.
FTWynn

2
Для подальшої довідки перегляньте пакет data.table R. freadФункція набагато швидше read.table. Використовуйте щось на зразок, x = fread(file_path_here, data.table=FALSE)щоб завантажити його як data.frameоб’єкт.
paleo13

Відповіді:


39

Моя спроба с readLines. Цей фрагмент коду створюється csvз обраними роками.

file_in <- file("in.csv","r")
file_out <- file("out.csv","a")
x <- readLines(file_in, n=1)
writeLines(x, file_out) # copy headers

B <- 300000 # depends how large is one pack
while(length(x)) {
    ind <- grep("^[^;]*;[^;]*; 20(09|10)", x)
    if (length(ind)) writeLines(x[ind], file_out)
    x <- readLines(file_in, n=B)
}
close(file_in)
close(file_out)

Це майже саме те, що я просто писав. Я відчуваю, що це також буде найкращою відповіддю, враховуючи обмеження пам’яті, змішані типи та неправильно сформовані рядки.
FTWynn

10

Я не фахівець у цьому, але ви можете спробувати спробувати MapReduce , що в принципі означало б підхід "розділяй і володарюй". R має кілька варіантів для цього, включаючи:

  1. mapReduce (чисто R)
  2. RHIPE (який використовує Hadoop ); див. приклад 6.2.2 в документації для прикладу піднабору файлів

Крім того, R пропонує кілька пакетів для роботи з великими даними, які виходять за межі пам'яті (на диск). Можливо, ви можете завантажити цілий набір даних в bigmemoryоб’єкт і повністю зменшити його в межах R. Набір інструментів для цього див. На http://www.bigmemory.org/ .


Хороша порада, але я не маю великого досвіду роботи з MapReduce та подібними. Мені доведеться це прочитати.
FTWynn

bigmemoryв такому випадку вам може бути простіше спробувати спершу.
Шейн

10

Чи існує подібний спосіб читати у файлах фрагменти за раз у R?

Так. Функція readChar () буде читати в блоці символів, не припускаючи, що вони закінчуються нулем. Якщо ви хочете читати дані в рядку за раз, ви можете використовувати readLines () . Якщо ви читаєте блок або рядок, виконуєте операцію, а потім виписуєте дані, ви можете уникнути проблеми з пам'яттю. Хоча, якщо ви хочете запустити великий екземпляр пам'яті на Amazon EC2, ви можете отримати до 64 ГБ оперативної пам'яті. Це має містити ваш файл, а також багато місця для обробки даних.

Якщо вам потрібна більша швидкість, тоді дуже корисна рекомендація Шейна використовувати Map Reduce. Однак якщо ви йдете шляхом використання великого екземпляру пам'яті на EC2, вам слід поглянути на багатоядерний пакет для використання всіх ядер на машині.

Якщо вам захочеться прочитати багато концертів розділених даних у R, вам слід принаймні дослідити пакет sqldf, який дозволяє імпортувати безпосередньо в sqldf з R, а потім оперувати даними всередині R. Я виявив, що sqldf є одним найшвидших способів імпорту концертів даних у R, як згадувалось у цьому попередньому питанні .


Я буду пам’ятати про екземпляр EC2, але на даний момент я повинен дотримуватися свого робочого столу, і це 2 Гб оперативної пам’яті. sqldf однозначно здається тим, що я мав на увазі. Однак він задихається і в неправильно сформованих рядках (Має бути 17 стовпців, але пара тисяч рядків має лише один). Чи вимагає це якийсь інший метод попередньої обробки, чи є варіант, якого я не маю?
FTWynn

6

Існує абсолютно новий пакет під назвою colbycol, який дозволяє читати лише ті змінні, які ви хочете, з величезних текстових файлів:

http://colbycol.r-forge.r-project.org/

Він передає будь-які аргументи до read.table, тому ця комбінація повинна дозволити вам досить жорстко підмножити.


6

ffПакет являє собою прозорий спосіб мати справу з великими файлами.

Ви можете побачити веб-сайт пакету та / або презентацію про нього.

Сподіваюся, це допоможе


5

Ви можете імпортувати дані до бази даних SQLite, а потім використовувати RSQLite для вибору підмножин.


Хороший план, але оскільки це, по суті, те, що sqldf робить за лаштунками, я би віддав перевагу цьому. Якщо немає кращого способу обробки неправильно сформованих рядків, якщо ви використовуєте прямий RSQLite?
FTWynn

5

А як щодо використання readrта read_*_chunkedсім’ї?

Отже, у вашому випадку:

testfile.csv

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5
lol
Ada County;NC;2013;1;FIRE;Financial;Banks;82.5

Фактичний код

require(readr)
f <- function(x, pos) subset(x, Year %in% c(2009, 2010))
read_csv2_chunked("testfile.csv", DataFrameCallback$new(f), chunk_size = 1)

Це стосується fкожного шматка, запам'ятовуючи назви кол-кодів та об'єднуючи відфільтровані результати в кінці. Подивіться, ?callbackщо є джерелом цього прикладу.

Це призводить до:

# A tibble: 2 × 8
      County State  Year Quarter Segment `Sub-Segment` `Sub-Sub-Segment`   GDP
*      <chr> <chr> <int>   <int>   <chr>         <chr>             <chr> <dbl>
1 Ada County    NC  2009       4    FIRE     Financial             Banks   801
2 Ada County    NC  2010       1    FIRE     Financial             Banks   825

Ви навіть можете збільшити, chunk_sizeале в цьому прикладі є лише 4 рядки.



3

Можливо, ви можете перейти на MySQL або PostgreSQL, щоб уникнути обмежень MS Access.

Досить легко підключити R до цих систем за допомогою роз'єму бази даних на основі DBI (доступний на CRAN).


Немає кращого варіанту використання кращих інструментів баз даних, але оскільки це буде вимагати адміністративних клопотів (треба любити ці адміністративні правила у великих компаніях), я намагаюся дотримуватися того, що маю. Крім того, я прагну до якомога меншої кількості перетворень між отриманим текстовим файлом.
FTWynn

3

scan () має як аргумент nlines, так і аргумент пропуску. Чи є якась причина, за допомогою якої ви можете просто читати шматок рядків за час, перевіряючи дату, чи це підходить? Якщо вхідний файл упорядкований за датою, ви можете зберегти індекс, який повідомляє вам, яким має бути ваш пропуск та рядки, що пришвидшить процес у майбутньому.


Я перевірю це, але файл не упорядкований нічим корисним, як дата. Здається, провайдери вважають, що важливіше сортувати за тим, в якому регіоні знаходиться даний округ. / Зітхання ...
FTWynn

Думаю, ви неправильно зрозуміли його пропозицію: прочитайте фрагмент файлу фрагментом за фрагментом та витягніть із кожного фрагмента лише потрібні вам рядки. Файли не потрібно замовляти.
Карл Форнер

1

У наші дні 3,5 ГБ просто не так вже й велике, я можу отримати доступ до машини з 244 ГБ оперативної пам’яті (r3,8xlarge) в хмарі Amazon за 2,80 доларів на годину. Скільки годин вам знадобиться, щоб зрозуміти, як вирішити проблему за допомогою рішень типу великих даних? Скільки коштує ваш час? Так, вам знадобиться година чи дві, щоб зрозуміти, як використовувати AWS - але ви можете навчитися основам на вільному рівні, завантажити дані та прочитати перші 10 тис. Рядків у R, щоб перевірити, чи він працює, а потім ви можете запустити великий екземпляр пам'яті, такий як r3.8xlarge, і прочитайте все це! Тільки моя 2c.


0

Зараз, 2017, я б запропонував піти на spark і sparkR.

  • синтаксис можна записати простим, досить подібним до dplyr способом

  • він досить добре підходить для малої пам’яті (невеликий у сенсі 2017 року)

Однак почати це може бути лякаючим досвідом ...


-3

Я хотів би взяти БД, а потім зробити кілька запитів для вилучення потрібних зразків через DBI

Будь ласка, уникайте імпорту файлу CSV розміром 3,5 ГБ у SQLite. Або принаймні двічі перевірте, чи ВАШЕ ВЕЛИЧЕЗНЕ базу даних відповідає лімітам SQLite, http://www.sqlite.org/limits.html

Це надзвичайно великий БД у вас є. Я б пішов на MySQL, якщо вам потрібна швидкість. Але будьте готові чекати багато годин, поки імпорт закінчиться. Якщо у вас немає якогось нетрадиційного обладнання або ви не пишете з майбутнього ...

EC2 Amazon може бути хорошим рішенням також для створення сервера серверів, що працює під управлінням R та MySQL.

мої дві скромні копійки варті.


18
Який розмір 3,5 Гб для sqlite? Поки ви використовуєте відповідну файлову систему, проблем не повинно бути (я регулярно використовую> 30 Гб sqlite dbs для однокористувацьких додатків)
Аарон Стейтем
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.