Документация Engee

Примеры

Кодировки символов, отличные от UTF-8

# Предположим, имеются текстовые данные в формате CSV, закодированные в ISO-8859-1
# Я загружаю пакет StringEncodings, который предоставляет функции преобразования кодировок
using CSV, StringEncodings

# Я открываю файл `iso8859_encoded_file.csv` с кодировкой `enc"ISO-8859-1"`
# и передаю открытый объект IO в функцию `CSV.File`, которая считывает все
# входные данные во временный файл, а затем анализирует данные из него
file = CSV.File(open("iso8859_encoded_file.csv", enc"ISO-8859-1"))

# Чтобы вместо этого преобразование кодировки происходило в памяти, передайте
# `buffer_in_memory=true`; это ускорит процесс, но, что очевидно, приведет
# к большему расходованию памяти вместо диска, на котором размещается временный файл
file = CSV.File(open("iso8859_encoded_file.csv", enc"ISO-8859-1"); buffer_in_memory=true)

Конкатенация нескольких наборов входных данных

using CSV

# В данном случае имеется вектор наборов входных данных с разделителями, имеющих
# одинаковую схему (одинаковые имена и типы столбцов). Я хочу обработать все
# входные данные одновременно и объединить их по вертикали в одну «длинную» таблицу.
data = [
    "a,b,c\n1,2,3\n4,5,6\n",
    "a,b,c\n7,8,9\n10,11,12\n",
    "a,b,c\n13,14,15\n16,17,18",
]

# Я могу просто передать объект `Vector` с входными данными, в данном случае `IOBuffer(::String)`, но это
# также может быть `Vector` с любым допустимым источником входных данных, например `AbstractVector{UInt8}`,
# именами файлов, `IO` и т. д. Каждый набор входных данных обрабатывается в отдельном потоке, а затем
# результаты объединяются по вертикали в один объект `CSV.File`. Столбцы каждого потока
# «лениво» объединяются с использованием типа `ChainedVector`. Как и обычно, если мы хотим
# отправить проанализированные столбцы напрямую в функцию-приемник, можно использовать `CSV.read`, например
# `df = CSV.read(map(IOBuffer, data), DataFrame)`.
f = CSV.File(map(IOBuffer, data))

Входные данные, сжатые gzip

# Если имеются текстовые данные в формате CSV, сжатые с помощью gzip,
# дополнительных пакетов не требуется; CSV.jl может распаковывать данные автоматически
using CSV

# Передадим имя входного файла, сжатого gzip, напрямую; данные будут распакованы во
# временный файл, а затем сопоставлены с памятью как байтовый буфер для анализа
file = CSV.File("data.gz")

# Чтобы вместо этого распаковка происходила в памяти, передайте
# `buffer_in_memory=true`; это ускорит процесс, но, что очевидно, приведет
# к большему расходованию памяти вместо диска, на котором размещается временный файл
file = CSV.File("data.gz"; buffer_in_memory=true)

Данные с разделителями в виде строки

using CSV

# У меня есть данные в формате CSV в виде строки, которые нужно проанализировать
data = """
a,b,c
1,2,3
4,5,6
"""

# Если вызвать `IOBuffer` для строки, будет возвращен объект IO в памяти,
# содержащий строковые данные, которые можно передать в `CSV.File` для анализа
file = CSV.File(IOBuffer(data))

Данные из Интернета (по URL-адресу)

# Предположим, нужно считать данные с разделителями из источника в Интернете
# Один из вариантов — воспользоваться пакетом HTTP.jl
using CSV, HTTP

# Сначала я выполняю веб-запрос, чтобы получить данные посредством `HTTP.get` по `url`
http_response = HTTP.get(url)

# Затем можно получить доступ к данным ответа как к `Vector{UInt8}` и передать
# их напрямую в `CSV.File` для анализа
file = CSV.File(http_response.body)

# В версиях Julia 1.6 и выше есть и другой вариант — пакет Downloads из stdlib
using Downloads
http_response = Downloads.download(url)

# По умолчанию `Downloads.download` записывает данные ответа во временный файл,
# который затем можно передать в `CSV.File` для анализа
file = CSV.File(http_response)

Считывание из ZIP-файла

using ZipFile, CSV, DataFrames

a = DataFrame(a = 1:3)
CSV.write("a.csv", a)

# Упакуем CSV-файл в ZIP-архив; пользователи Windows, у которых нет zip по пути PATH, могут сделать это вручную
# или выполнить запись непосредственно в ZIP-архив, как показано ниже
;zip a.zip a.csv

# Другой вариант — непосредственная запись в ZIP-архив (без предварительного создания неупакованного CSV-файла)
z = ZipFile.Writer("a2.zip")
f = ZipFile.addfile(z, "a.csv", method=ZipFile.Deflate)
a |> CSV.write(f)
close(z)

# Считаем файл из ZIP-архива
z = ZipFile.Reader("a.zip") # или a2.zip

# Находим нужный файл в ZIP-архиве
a_file_in_zip = filter(x->x.name == "a.csv", z.files)[1]

a_copy = CSV.File(a_file_in_zip) |> DataFrame

a == a_copy

close(z)

Имена столбцов во второй строке

using CSV

data = """
descriptive row with information about the file that we'd like to ignore
a,b,c
1,2,3
4,5,6
"""

# Если передать header=2, при анализе первая строка полностью пропускается
# Анализируются имена столбцов во второй строке, а затем по умолчанию предполагается,
# что данные начинаются со строки после имен столбцов (в данном случае с третьей)
# В данном случае это верно
file = CSV.File(IOBuffer(data); header=2)

Данные без имен столбцов

using CSV

# В данном случае в данных вообще нет имен столбцов
data = """
1,2,3
4,5,6
"""

# Если передать `header=false`, при анализе поиск имен столбцов не производится
# Вместо этого сразу начинают анализироваться данные и при необходимости
# генерируются имена столбцов, например `Column1`, `Column2` и `Column3`, как в данном случае
file = CSV.File(IOBuffer(data); header=false)

Указание имен столбцов вручную

using CSV

# В данном случае в данных вообще нет имен столбцов
data = """
1,2,3
4,5,6
"""

# Вместо передачи `header=false` и получения автоматически сгенерированных имен столбцов
# мы можем передать имена столбцов самостоятельно
file = CSV.File(IOBuffer(data); header=["a", "b", "c"])

# Имена столбцов можно также передать как символы; всегда создается копия
# указанных вручную имен столбцов, которая затем преобразуется в `Vector{Symbol}`
file = CSV.File(IOBuffer(data); header=[:a, :b, :c])

Имена столбцов в нескольких строках

using CSV

# В данном случае мы имеем имена столбцов `col_a`, `col_b` и `col_c`,
# но они разнесены на первую и вторую строки
data = """
col,col,col
a,b,c
1,2,3
4,5,6
"""

# Если передать коллекцию целых чисел, будут проанализированы строки с соответствующими номерами
# и значения из них объединены для каждого столбца через символ `_`
file = CSV.File(IOBuffer(data); header=[1, 2])

Нормализация имен столбцов

using CSV

# В данном случае данные представляют собой отдельные буквы, а имена столбцов — «1», «2» и «3»
# Отдельная цифра не является допустимым идентификатором в Julia, то есть
# операция `1 = 2 + 2`, где `1` — имя переменной, недопустима
data = """
1,2,3
a,b,c
d,e,f
h,i,j
"""

# Чтобы имена столбцов стали допустимыми идентификаторами, можно передать
# `normalizenames=true`, в результате чего имена столбцов примут вид "_1", "_2" и "_3"
# Имейте в виду, что это не обязательно, но в некоторых случаях может быть удобно
file = CSV.File(IOBuffer(data); normalizenames=true)

# К первому столбцу можно получить доступ так
file._1

# Еще один пример случая, где может потребоваться нормализация, — имена столбцов с пробелами
data = """
column one,column two, column three
1,2,3
4,5,6
"""

# В результате нормализации получатся имена столбцов column_one, column_two и column_three
file = CSV.File(IOBuffer(data); normalizenames=true)

Переход к определенной строке, с которой начинаются данные

using CSV

# В наших данных первую строку следует пропустить; кроме того, в них нет
# имен столбцов, поэтому их следует сгенерировать автоматически
data = """
descriptive row that gives information about the data that we'd like to ignore
1,2,3
4,5,6
"""

# Когда в данных нет имен столбцов, мы сначала передаем `header=false`; само по себе
# это приведет к тому, что анализ фактических данных начнется со строки 1;
# однако мы хотим пропустить первую строку, поэтому передаем `skipto=2`;
# для столбцов будут сгенерированы имена наподобие `Column1`, `Column2`, `Column3`
file = CSV.File(IOBuffer(data); header=false, skipto=2)

Пропуск ненужных конечных строк

using CSV

# У нас есть данные с именами столбцов a, b и c,
# но в конце имеются две строки, которые следует пропустить при анализе,
# так как они не содержат надлежащих разделителей
data = """
a,b,c
1,2,3
4,5,6
7,8,9
totals: 12, 15, 18
grand total: 45
"""

# Передавая `footerskip=2`, мы указываем, что анализ данных должен начаться с конца;
# после считывания двух строк, содержимое которых игнорируется, устанавливается маркер конечной позиции,
# в которой должен завершиться процесс анализа
file = CSV.File(IOBuffer(data); footerskip=2)

Считывание транспонированных данных

using CSV

# Наши данные транспонированы, то есть имена столбцов находятся в первом столбце,
# данные столбца a — в первой строке, данные столбца b —
# во второй и т. д.
data = """
a,1,4,7
b,2,5,8
c,3,6,9
"""

# Если передать `transpose=true`, имена столбцов будут считываться из первого
# столбца данных, после чего каждая строка будет анализироваться как отдельный столбец
file = CSV.File(IOBuffer(data); transpose=true)

Пропуск закомментированных строк

using CSV

# В данном случае имеется несколько строк, не содержащих данных; все они начинаются со строки «#»
data = """
# строка с описанием имен столбцов
a,b,c
# строка с описанием первой строки данных
1,2,3
# строка с описанием второй строки данных
4,5,6
"""

# Нам нужно пропустить эти «закомментированные» строки
file = CSV.File(IOBuffer(data); comment="#")

Пропуск пустых строк

using CSV

# Здесь между первой и второй строками с данными имеется «пробел»
# По умолчанию такие «пустые» строки игнорируются, но в нашем случае они служат
# для ввода строк, содержащих исключительно значения missing или null;
# поэтому, чтобы мы знали, сколько в данных отсутствующих значений,
# они не должны пропускаться
data = """
a,b,c
1,2,3

4,5,6
"""

# Если передать `ignoreemptyrows=false`, пустые строки будут анализироваться так,
# как если бы они содержали значение `missing` для каждого столбца
file = CSV.File(IOBuffer(data); ignoreemptyrows=true)

Включение и исключение столбцов

using CSV

# Это простой набор данных, однако известно, что столбец b не нужен,
# поэтому, чтобы сэкономить время, мы хотели бы полностью игнорировать его при анализе
data = """
a,b,c
1,2,3
4,5,6
7,8,9
"""

# Аргументы выбора (select) и исключения (drop) можно задать несколькими способами,
# поэтому мы приведем пример для каждого; начнем с выбора столбцов
# a и c, которые нужно включить в анализ
file = CSV.File(IOBuffer(data); select=[1, 3])
file = CSV.File(IOBuffer(data); select=[:a, :c])
file = CSV.File(IOBuffer(data); select=["a", "c"])
file = CSV.File(IOBuffer(data); select=[true, false, true])
file = CSV.File(IOBuffer(data); select=(i, nm) -> i in (1, 3))
# А теперь примеры исключения: теперь мы указываем столбцы,
# которые не нужно анализировать
file = CSV.File(IOBuffer(data); drop=[2])
file = CSV.File(IOBuffer(data); drop=[:b])
file = CSV.File(IOBuffer(data); drop=["b"])
file = CSV.File(IOBuffer(data); drop=[false, true, false])
file = CSV.File(IOBuffer(data); drop=(i, nm) -> i == 2)

Ограничение количества строк данных

using CSV

# Здесь у нас довольно много строк данных (естественно, в сравнении с другими примерами),
# но известно, что для анализа требуются только три первые,
# поэтому вместо того, чтобы тратить время на анализ всего файла, нужно
# прочесть только три первые строки и пропустить остальные
data = """
a,b,c
1,2,3
4,5,6
7,8,9
10,11,12
13,14,15
"""

# После считывания трех строк анализ преждевременно
# завершается, так что остальные данные не анализируются
file = CSV.File(IOBuffer(data); limit=3)

Указание пользовательских строк, соответствующих отсутствию значения

using CSV

# В этом примере данных в первом столбце отсутствие значения обозначено строкой -999,
# а в столбце score — строкой NA
# Нам нужно сделать так, чтобы при анализе данных оба эти значения заменялись на `missing`
data = """
code,age,score
0,21,3.42
1,42,6.55
-999,81,NA
-999,83,NA
"""

# Если передать missingstring=["-999", "NA"], в ходе анализа каждая ячейка проверяется на
# соответствие указанным строкам; в случае соответствия задается значение `missing`
file = CSV.File(IOBuffer(data); missingstring=["-999", "NA"])

Строка-разделитель

using CSV

# Наши данные содержат два столбца, которые разделены
# двумя двоеточиями ("::")
data = """
col1::col2
1::2
3::4
"""

# В качестве разделителя можно указать отдельный символ или строку
file = CSV.File(IOBuffer(data); delim="::")

Файлы фиксированной длины

using CSV

# Это пример данных фиксированной длины, в которых в каждой
# строке каждого столбца содержится одинаковое
# количество символов. Поля заполняются дополнительными
# разделителями (в данном случае `' '`), чтобы в каждом столбце
# всегда было одно и то же количество символов
data = """
col1    col2 col3
123431  2    3421
2355    346  7543
"""
# Помимо `delim`, мы можем передать
# `ignorerepeated=true`, чтобы при анализе
#несколько разделителей подряд интерпретировались как один
# разделитель.
file = CSV.File(IOBuffer(data); delim=' ', ignorerepeated=true)

Отключение анализа заключенных в кавычки ячеек

using CSV

# По умолчанию такие ячейки, как, например, ячейка в столбце 1, строке 2,
# рассматриваются как заключенные в кавычки, то есть начинающиеся
# с символа кавычки («"») и заканчивающиеся им. После анализа
# значение будет без кавычек
# Однако нам может требоваться исходное значение в полном виде,
# то есть с кавычками
data = """
a,b,c
"hey",2,3
there,4,5
sailor,6,7
"""

# Чтобы отключить обнаружение заключенных в кавычки ячеек,
# передадим `quoted=false`
file = CSV.File(IOBuffer(data); quoted=false)

Заключенные в кавычки и экранированные поля

using CSV

# В этом примере данных есть несколько заключенных в кавычки полей. Это означает, что значение поля начинается с символа `quotechar` и заканчивается им (либо
# начинается с `openquotechar` и заканчивается `closequotechar`). В поля в кавычках можно добавлять символы, которые в противном случае
# анализировались бы особым образом, например разделители или символы новой строки. Если поле заключено в кавычки, при анализе такие особые
# символы интерпретируются как обычные, пока не будет достигнута закрывающая кавычка. Если в поле в кавычках необходимо использовать сам символ
# кавычки, следует применить escape-символ. Он указывает, что при поиске закрывающей кавычки следующий символ
# должен игнорироваться. В примерах синтаксиса именованные аргументы передаются явным образом, однако для них заданы значения
# по умолчанию, поэтому если просто вызвать `CSV.File(IOBuffer(data))`, анализ будет выполнен успешно.
data = """
col1,col2
"quoted field with a delimiter , inside","quoted field that contains a \\n newline and ""inner quotes\\\"\\\"\\\"
unquoted field,unquoted field with "inner quotes"
"""

file = CSV.File(IOBuffer(data); quotechar='"', escapechar='"')

file = CSV.File(IOBuffer(data); openquotechar='"' closequotechar='"', escapechar='"')

Формат даты

using CSV

# В этом файле столбец `date` содержит даты в формате `yyyy/mm/dd` (гггг/мм/дд). Мы можем передать такую строку в
# именованном аргументе `dateformat`, чтобы такой формат применялся при анализе столбцов типа `Date` или `DateTime`. Обратите внимание, что в настоящее время
# в `dateformat` можно передать только одну строку, то есть одновременно нельзя проанализировать несколько строк
# типа `Date` или `DateTime` с разными форматами даты.
data = """
code,date
0,2019/01/01
1,2019/01/02
"""

file = CSV.File(IOBuffer(data); dateformat="yyyy/mm/dd")

Пользовательский десятичный разделитель

using CSV

# Во многих странах мира дробная часть десятичного числа отделяется запятой, а не точкой (`3,14` вместо `3.14`)
# . Для правильного анализа таких чисел можно передать именованный аргумент `decimal=','`. Обратите внимание, что в таких случаях может потребоваться
# явным образом передать `delim=';'`, так как анализатор, скорее всего, сочтет символ `','` разделителем.
data = """
col1;col2;col3
1,01;2,02;3,03
4,04;5,05;6,06
"""

file = CSV.File(IOBuffer(data); delim=';', decimal=',')

Разделитель групп разрядов

using CSV

# Во многих странах мира цифры слева от десятичного разделителя разбиваются на
# группы с помощью разделителя групп разрядов. Чтобы игнорировать эти разделители, можно передать именованный
# аргумент `groupmark`.
data = """
x y
1 2
2 1,729
3 87,539,319
"""

file = CSV.File(IOBuffer(data); groupmark=',')

Пользовательские маркеры групп

using CSV

# В некоторых ситуациях для группировки цифр в числе применяются не только разделители групп разрядов, но и другие разделители.
# Аргумент `groupmark` позволяет игнорировать такие разделители в кодировке ASCII
data = """
name;ssn;credit card number
Ayodele Beren;597-21-8366;5538-6111-0574-2633
Trinidad Shiori;387-35-5126;3017-9300-0776-5301
Ori Cherokee;731-12-4606;4682-5416-0636-3877
"""

file = CSV.File(IOBuffer(data); groupmark='-')

Пользовательские строки логических значений

using CSV

# По умолчанию как допустимые значения типа `Bool` анализатор интерпретирует только строки `true` и `false`. Чтобы добавить
# другие строки, можно передать `Vector{String}` в именованных аргументах `truestrings` и `falsestrings`.
data = """
id,paid,attended
0,T,TRUE
1,F,TRUE
2,T,FALSE
3,F,FALSE
"""

file = CSV.File(IOBuffer(data); truestrings=["T", "TRUE"], falsestrings=["F", "FALSE"])

Матричные данные

using CSV

# Этот файл содержит единичную матрицу 3x3 типа `Float64`. По умолчанию анализатор сам определяет разделитель и тип, но мы также
# можем явно передать `delim= ' '` и `types=Float64`, чтобы анализатор интерпретировал каждый столбец как `Float64`
# и ему не приходилось определять тип самостоятельно.
data = """
1.0 0.0 0.0
0.0 1.0 0.0
0.0 0.0 1.0
"""

file = CSV.File(IOBuffer(data); header=false)
file = CSV.File(IOBuffer(data); header=false, delim=' ', types=Float64)

# Наконец, можно преобразовать эти данные в объект Matrix, сначала прочитав их как DataFrame, а затем
# передав результат по цепочке функций в Matrix
using DataFrames
A = file|>DataFrame|>Matrix

# Другой вариант — просто воспользоваться CSV.Tables.matrix
B = file|>CSV.Tables.matrix # не требуется DataFrames

Указание типов

using CSV

# В этом файле в третьем столбце второй строки содержится недопустимое значение `invalid`. Предположим, что столбец все равно должен анализироваться как
# имеющий тип `Int`, а значение `invalid` должно игнорироваться. В примерах синтаксиса представлено несколько способов указать, что третий
# столбец должен интерпретироваться как `Int`: путем указания индекса столбца `3` или имени столбца в виде `Symbol` или `String`. Для каждого столбца можно также
# указать вектор `Vector` типов (его длина должна соответствовать длине столбцов в файле). Есть еще два
# именованных аргумента, которые управляют ходом анализа; в первых четырех примерах синтаксиса выводится предупреждение в виде
# `"warning: invalid Int64 value on row 2, column 3"`. В пятом примере передается аргумент `silencewarnings=true`, чтобы отключить
# вывод предупреждения. В последнем примере синтаксиса передается аргумент `strict=true`, что приводит к выдаче ошибки во время анализа.
data = """
col1,col2,col3
1,2,3
4,5,invalid
6,7,8
"""

file = CSV.File(IOBuffer(data); types=Dict(3 => Int))
file = CSV.File(IOBuffer(data); types=Dict(:col3 => Int))
file = CSV.File(IOBuffer(data); types=Dict("col3" => Int))
file = CSV.File(IOBuffer(data); types=[Int, Int, Int])
file = CSV.File(IOBuffer(data); types=[Int, Int, Int], silencewarnings=true)
file = CSV.File(IOBuffer(data); types=[Int, Int, Int], strict=true)


# В этом файле множество столбцов, и для всех них нужно указать один и тот же тип,
# кроме одного, у которого тип должен быть другой. Для этого можно использовать функцию,
# которая принимает индекс и имя столбца и на их основе определяет тип.
data = """
col1,col2,col3,col4,col5,col6,col7
1,2,3,4,5,6,7
0,2,3,4,5,6,7
1,2,3,4,5,6,7
"""
file = CSV.File(IOBuffer(data); types=(i, name) -> i == 1 ? Bool : Int8)
file = CSV.File(IOBuffer(data); types=(i, name) -> name == :col1 ? Bool : Int8)
# Кроме того, можно предоставить точное имя первого столбца и регулярное выражение, которому должны соответствовать остальные.
# Имейте в виду, что точное имя столбца всегда имеет приоритет над регулярным выражением.
file = CSV.File(IOBuffer(data); types=Dict(:col1 => Bool, r"^col\d" => Int8))

Сопоставление типов

using CSV

# В этом файле первый столбец содержит почтовые индексы США, которые нежелательно интерпретировать как `Int`, но анализатор поступит
# именно так. В первом примере синтаксиса мы передаем аргумент `typemap=IdDict(Int => String)`, который предписывает анализатору интерпретировать все обнаруженные столбцы `Int`
# как `String`. Во втором примере синтаксиса мы вместо этого вручную задаем тип столбца `zipcode`.
data = """
zipcode,score
03494,9.9
12345,6.7
84044,3.4
"""

file = CSV.File(IOBuffer(data); typemap=IdDict(Int => String))
file = CSV.File(IOBuffer(data); types=Dict(:zipcode => String))

Сжатие значений

using CSV

# В этом файле есть столбцы `id` и `code`. «Сжатие» значений типа `String`, то есть сопоставление каждого уникального значения с `UInt32`,
# может быть выгодным при выполнении различных операций с DataFrame и таблицами, например объединения и группировки. По умолчанию
# `pool=(0.2, 500)`, то есть строковые столбцы с меньшей мощностью сжимаются по умолчанию. Именованный аргумент `pool` обеспечивает
# более точный контроль: `pool=0.4` означает, что если 40 % значений столбца или менее являются уникальными, он сжимается.
data = """
id,code
A18E9,AT
BF392,GC
93EBC,AT
54EE1,AT
8CD2E,GC
"""

file = CSV.File(IOBuffer(data))
file = CSV.File(IOBuffer(data); pool=0.4)
file = CSV.File(IOBuffer(data); pool=0.6)

Сжатие нестроковых значений

using CSV

# В этом примере данных столбец `category` имеет целочисленный тип, но содержит ограниченный набор значений, так что сжатие
# может быть полезным. Например, с этим столбцом может потребоваться выполнить различные операции группировки и объединения DataFrame, которые могут быть
# эффективнее, если столбец будет иметь тип PooledVector. По умолчанию при передаче `pool=true` сжимаются столбцы только строковых типов;
# однако, передав вектор или словарь, можно указать, как должны сжиматься те или иные столбцы нестроковых типов.
data = """
category,amount
1,100.01
1,101.10
2,201.10
2,202.40
"""

file = CSV.File(IOBuffer(data); pool=Dict(1 => true))
file = CSV.File(IOBuffer(data); pool=[true, false])

Сжатие с абсолютным порогом

using CSV

# В этом файле есть столбцы `id` и `code`. «Сжатие» значений типа `String`, то есть сопоставление каждого уникального значения с `UInt32`,
# может быть выгодным при выполнении различных операций с DataFrame и таблицами, например объединения и группировки. По умолчанию
# `pool=(0.2, 500)`, то есть строковые столбцы с меньшей мощностью сжимаются по умолчанию. Именованный аргумент `pool` обеспечивает
# более точный контроль: `pool=(0.5, 2)` означает, что если в столбце два или менее уникальных значений, а общее количество уникальных значений составляет менее 50 % всех значений, столбец сжимается.
data = """
id,code
A18E9,AT
BF392,GC
93EBC,AT
54EE1,AT
8CD2E,GC
"""

file = CSV.File(IOBuffer(data); pool=(0.5, 2))