Начало работы
В оставшейся части этого руководства мы будем считать, что вы установили пакет DataFrames и уже ввели using DataFrames
, чтобы добавить все соответствующие переменные в текущее пространство имен.
По умолчанию DataFrames.jl ограничивает количество строк и столбцов при отображении фрейма данных в Jupyter Notebook до 25 и 100, соответственно. Это поведение можно переопределить, изменив значения переменных Или же можно задать максимальное количество строк фрейма данных для вывода равным Пакет PrettyTables.jl отрисовывает |
Тип DataFrame
Объекты типа DataFrame
представляют таблицу данных в виде ряда векторов, каждый из которых соответствует столбцу или переменной. Самый простой способ построения DataFrame
— это передача векторов столбцов с помощью именованных аргументов или пар:
julia> using DataFrames
julia> DataFrame(a=1:4, b=["M", "F", "F", "M"]) # конструктор именованных аргументов
4×2 DataFrame
Row │ a b
│ Int64 String
─────┼───────────────
1 │ 1 M
2 │ 2 F
3 │ 3 F
4 │ 4 M
Ниже приведены примеры других часто используемых способов построения фреймов данных:
julia> DataFrame((a=[1, 2], b=[3, 4])) # конструктор таблиц Tables.jl из именованного кортежа векторов
2×2 DataFrame
Row │ a b
│ Int64 Int64
─────┼──────────────
1 │ 1 3
2 │ 2 4
julia> DataFrame([(a=1, b=0), (a=2, b=0)]) # конструктор таблиц Tables.jl из вектора именованных кортежей
2×2 DataFrame
Row │ a b
│ Int64 Int64
─────┼──────────────
1 │ 1 0
2 │ 2 0
julia> DataFrame("a" => 1:2, "b" => 0) # конструктор пар
2×2 DataFrame
Row │ a b
│ Int64 Int64
─────┼──────────────
1 │ 1 0
2 │ 2 0
julia> DataFrame([:a => 1:2, :b => 0]) # конструктор вектора пар
2×2 DataFrame
Row │ a b
│ Int64 Int64
─────┼──────────────
1 │ 1 0
2 │ 2 0
julia> DataFrame(Dict(:a => 1:2, :b => 0)) # конструктор словаря
2×2 DataFrame
Row │ a b
│ Int64 Int64
─────┼──────────────
1 │ 1 0
2 │ 2 0
julia> DataFrame([[1, 2], [0, 0]], [:a, :b]) # конструктор вектора векторов
2×2 DataFrame
Row │ a b
│ Int64 Int64
─────┼──────────────
1 │ 1 0
2 │ 2 0
julia> DataFrame([1 0; 2 0], :auto) # конструктор матриц
2×2 DataFrame
Row │ x1 x2
│ Int64 Int64
─────┼──────────────
1 │ 1 0
2 │ 2 0
Столбцы можно извлекать напрямую (т. е. без копирования), используя df.col
, df."col"
, df[!, :col]
или df[!, "col"]
(это правило относится к получению данных из фрейма данных, а не к записи данных во фрейм данных). Два последних синтаксиса более гибкие, поскольку позволяют передавать переменную, содержащую имя столбца, а не только литеральное имя. Обратите внимание, что имена столбцов могут быть как символами (записываются как :col
, :var"col"
или Symbol("col")
), так и строками (записываются как "col"
). В формах df."col"
и :var"col"
интерполяция переменных в строку с помощью $
не работает. Столбцы также можно извлекать с помощью целочисленного индекса, определяющего их позицию.
Поскольку df[!, :col]
не создает копию, изменение элементов вектора столбцов, возвращаемого этим синтаксисом, повлияет на значения, хранящиеся в исходном df
. Для получения копии столбца используйте df[:, :col]
: изменение вектора, возвращаемого этим синтаксисом, не изменяет df
.
julia> df = DataFrame(A=1:4, B=["M", "F", "F", "M"])
4×2 DataFrame
Row │ A B
│ Int64 String
─────┼───────────────
1 │ 1 M
2 │ 2 F
3 │ 3 F
4 │ 4 M
julia> df.A
4-element Vector{Int64}:
1
2
3
4
julia> df."A"
4-element Vector{Int64}:
1
2
3
4
julia> df.A === df[!, :A]
true
julia> df.A === df[:, :A]
false
julia> df.A == df[:, :A]
true
julia> df.A === df[!, "A"]
true
julia> df.A === df[:, "A"]
false
julia> df.A == df[:, "A"]
true
julia> df.A === df[!, 1]
true
julia> df.A === df[:, 1]
false
julia> df.A == df[:, 1]
true
julia> firstcolumn = :A
:A
julia> df[!, firstcolumn] === df.A
true
julia> df[:, firstcolumn] === df.A
false
julia> df[:, firstcolumn] == df.A
true
Имена столбцов можно получить в виде строк с помощью функции names
:
julia> names(df)
2-element Vector{String}:
"A"
"B"
Вы также можете фильтровать имена столбцов, передавая условие селектора столбцов в качестве второго аргумента. См. docstring names
с подробным списком доступных условий. Приведем несколько примеров:
julia> names(df, r"A") # селектор регулярных выражений
1-element Vector{String}:
"A"
julia> names(df, Int) # селектор, использующий тип элементов столбцов
1-element Vector{String}:
"A"
julia> names(df, Not(:B)) # селектор, сохраняющий все столбцы, кроме :B
1-element Vector{String}:
"A"
Чтобы получить имена столбцов в виде Symbol
, используйте функцию propertynames
:
julia> propertynames(df)
2-element Vector{Symbol}:
:A
:B
DataFrames.jl позволяет для удобства использовать |
Создание по столбцам
Также можно начать с пустого DataFrame
и добавлять в него столбцы по одному:
julia> df = DataFrame()
0×0 DataFrame
julia> df.A = 1:8
1:8
julia> df[:, :B] = ["M", "F", "F", "M", "F", "M", "M", "F"]
8-element Vector{String}:
"M"
"F"
"F"
"M"
"F"
"M"
"M"
"F"
julia> df[!, :C] .= 0
8-element Vector{Int64}:
0
0
0
0
0
0
0
0
julia> df
8×3 DataFrame
Row │ A B C
│ Int64 String Int64
─────┼──────────────────────
1 │ 1 M 0
2 │ 2 F 0
3 │ 3 F 0
4 │ 4 M 0
5 │ 5 F 0
6 │ 6 M 0
7 │ 7 M 0
8 │ 8 F 0
Построенный таким образом DataFrame
имеет 8 строк и 3 столбца. Это можно проверить с помощью функции size
:
julia> size(df, 1)
8
julia> size(df, 2)
3
julia> size(df)
(8, 3)
В приведенном выше примере обратите внимание, что выражение df[!, :C] .= 0
создало новый столбец во фрейме данных путем трансляции скаляра.
При задании столбца фрейма данных синтаксисы df[!, :C]
и df.C
эквивалентны, и они заменят (или создадут) столбец :C
в df
. Это отличается от использования df[:, :C]
для установки столбца во фрейме данных, который обновляет содержимое столбца на месте, если он уже существует.
Вот пример, демонстрирующий это различие. Попробуем изменить столбец :B
на двоичную переменную.
julia> df[:, :B] = df.B .== "F"
ERROR: MethodError: Cannot `convert` an object of type Bool to an object of type String
julia> df[:, :B] .= df.B .== "F"
ERROR: MethodError: Cannot `convert` an object of type Bool to an object of type String
Приведенные выше операции не работают, поскольку при использовании :
в качестве селектора строк столбец :B
обновляется на месте, а он поддерживает только хранение строк.
С другой стороны, работает следующее.
julia> df.B = df.B .== "F"
8-element BitVector:
0
1
1
0
1
0
0
1
julia> df
8×3 DataFrame
Row │ A B C
│ Int64 Bool Int64
─────┼─────────────────────
1 │ 1 false 0
2 │ 2 true 0
3 │ 3 true 0
4 │ 4 false 0
5 │ 5 true 0
6 │ 6 false 0
7 │ 7 false 0
8 │ 8 true 0
Как видите, поскольку мы использовали df.B
в правой части присваивания, столбец :B
был заменен. Тот же эффект получился бы в том случае, если бы вместо этого мы использовали df[!, :B]
или транслированное присваивание .=
.
В разделе Indexing руководства вы найдете подробную информацию обо всех доступных вариантах индексирования.
Построчное создание
DataFrame
можно также заполнять построчно. Давайте построим пустой фрейм данных с двумя столбцами (обратите внимание, что первый столбец может содержать только целые числа, а второй — только строки):
julia> df = DataFrame(A=Int[], B=String[])
0×2 DataFrame
Row │ A B
│ Int64 String
─────┴───────────────
Затем можно добавить строки в виде кортежей или векторов, где порядок элементов соответствует порядку столбцов. Чтобы добавить новые строки в конец фрейма данных, используйте push!
:
julia> push!(df, (1, "M"))
1×2 DataFrame
Row │ A B
│ Int64 String
─────┼───────────────
1 │ 1 M
julia> push!(df, [2, "N"])
2×2 DataFrame
Row │ A B
│ Int64 String
─────┼───────────────
1 │ 1 M
2 │ 2 N
Строки также могут быть добавлены как Dict
, где ключи словаря совпадают с именами столбцов:
julia> push!(df, Dict(:B => "F", :A => 3))
3×2 DataFrame
Row │ A B
│ Int64 String
─────┼───────────────
1 │ 1 M
2 │ 2 N
3 │ 3 F
Обратите внимание, что построчное построение DataFrame
значительно менее производительно, чем построение всего фрейма данных сразу или столбец за столбцом. Для многих вариантов использования это не имеет значения, но может быть важно для очень больших DataFrame
.
Чтобы добавить строки в начало фрейма данных, используйте pushfirst!
, а чтобы вставить строку в произвольное место, используйте insert!
.
Построение из другого табличного типа
DataFrames поддерживает интерфейс Tables.jl для взаимодействия с табличными данными. Это означает, что DataFrame
можно использовать в качестве «источника» для любого пакета, который ожидает входные данные интерфейса Tables.jl (пакеты формата файлов, пакеты работы с данными и т. д.). DataFrame
также может быть приемником для любых входных данных интерфейса Tables.jl. Вот некоторые примеры использования:
df = DataFrame(a=[1, 2, 3], b=[:a, :b, :c])
# запись DataFrame в CSV-файл
CSV.write("dataframe.csv", df)
# хранение DataFrame в таблице базы данных SQLite
SQLite.load!(df, db, "dataframe_table")
# преобразование DataFrame с помощью пакета Query.jl
df = df |> @map({a=_.a + 1, _.b}) |> DataFrame
Частным случаем коллекции, поддерживающей интерфейс Tables.jl, является вектор NamedTuple
:
julia> v = [(a=1, b=2), (a=3, b=4)]
2-element Vector{NamedTuple{(:a, :b), Tuple{Int64, Int64}}}:
(a = 1, b = 2)
(a = 3, b = 4)
julia> df = DataFrame(v)
2×2 DataFrame
Row │ a b
│ Int64 Int64
─────┼──────────────
1 │ 1 2
2 │ 3 4
Вы можете легко преобразовать фрейм данных обратно в вектор NamedTuple
:
julia> using Tables
julia> Tables.rowtable(df)
2-element Vector{NamedTuple{(:a, :b), Tuple{Int64, Int64}}}:
(a = 1, b = 2)
(a = 3, b = 4)