Работа с фреймами данных
Изучение данных
Вывод объектов DataFrame
по умолчанию включает только выборку строк и столбцов, которая помещается на экране:
julia> using DataFrames
julia> df = DataFrame(A=1:2:1000, B=repeat(1:10, inner=50), C=1:500)
500×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 1 1
2 │ 3 1 2
3 │ 5 1 3
4 │ 7 1 4
5 │ 9 1 5
6 │ 11 1 6
7 │ 13 1 7
8 │ 15 1 8
⋮ │ ⋮ ⋮ ⋮
494 │ 987 10 494
495 │ 989 10 495
496 │ 991 10 496
497 │ 993 10 497
498 │ 995 10 498
499 │ 997 10 499
500 │ 999 10 500
485 rows omitted
Параметры вывода можно настроить, вызвав функцию show
вручную: show(df, allrows=true)
выводит все строки, даже если они не помещаются на экране, а show(df, allcols=true)
делает то же самое для столбцов.
Функции first
и last
можно использовать для просмотра первой и последней строк фрейма данных (соответственно):
julia> first(df, 6)
6×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 1 1
2 │ 3 1 2
3 │ 5 1 3
4 │ 7 1 4
5 │ 9 1 5
6 │ 11 1 6
julia> last(df, 6)
6×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 989 10 495
2 │ 991 10 496
3 │ 993 10 497
4 │ 995 10 498
5 │ 997 10 499
6 │ 999 10 500
Также обратите внимание, что при выводе DataFrame
на консоль или отрисовке в формате HTML (например, в Jupyter Notebook) вы получаете информацию о типах элементов, содержащихся в его столбцах. Например, здесь
julia> using CategoricalArrays
julia> DataFrame(a=1:2, b=[1.0, missing],
c=categorical('a':'b'), d=[1//2, missing])
2×4 DataFrame
Row │ a b c d
│ Int64 Float64? Cat… Rational…?
─────┼────────────────────────────────────
1 │ 1 1.0 a 1//2
2 │ 2 missing b missing
мы видим, что
-
первый столбец
:a
может содержать элементы типаInt64
; -
второй столбец
:b
может содержатьFloat64
илиMissing
, на что указывает символ?
, отображаемый после имени типа; -
третий столбец
:c
может содержать категориальные данные. Здесь мы видим символ…
, который указывает на то, что реальное имя типа было длинным и было усечено; -
информация о типе в четвертом столбце
:d
представляет ситуацию, когда имя является усеченным, а тип допускаетMissing
.
Получение подмножества
Синтаксис индексирования
С помощью синтаксиса индексирования можно извлекать конкретные подмножества фрейма данных, подобно матрицам. В разделе Indexing руководства вы найдете подробную информацию обо всех доступных вариантах. Здесь мы рассмотрим самые основные.
Двоеточие :
указывает на то, что все элементы (строки или столбцы в зависимости от их позиции) должны быть сохранены:
julia> df[1:3, :]
3×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 1 1
2 │ 3 1 2
3 │ 5 1 3
julia> df[[1, 5, 10], :]
3×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 1 1
2 │ 9 1 5
3 │ 19 1 10
julia> df[:, [:A, :B]]
500×2 DataFrame
Row │ A B
│ Int64 Int64
─────┼──────────────
1 │ 1 1
2 │ 3 1
3 │ 5 1
4 │ 7 1
5 │ 9 1
6 │ 11 1
7 │ 13 1
8 │ 15 1
⋮ │ ⋮ ⋮
494 │ 987 10
495 │ 989 10
496 │ 991 10
497 │ 993 10
498 │ 995 10
499 │ 997 10
500 │ 999 10
485 rows omitted
julia> df[1:3, [:B, :A]]
3×2 DataFrame
Row │ B A
│ Int64 Int64
─────┼──────────────
1 │ 1 1
2 │ 1 3
3 │ 1 5
julia> df[[3, 1], [:C]]
2×1 DataFrame
Row │ C
│ Int64
─────┼───────
1 │ 3
2 │ 1
Обратите внимание, что df[!, [:A]]
и df[:, [:A]]
возвращают объект DataFrame
, а df[!, :A]
и df[:, :A]
— вектор:
julia> df[!, [:A]]
500×1 DataFrame
Row │ A
│ Int64
─────┼───────
1 │ 1
2 │ 3
3 │ 5
4 │ 7
5 │ 9
6 │ 11
7 │ 13
8 │ 15
⋮ │ ⋮
494 │ 987
495 │ 989
496 │ 991
497 │ 993
498 │ 995
499 │ 997
500 │ 999
485 rows omitted
julia> df[!, [:A]] == df[:, [:A]]
true
julia> df[!, :A]
500-element Vector{Int64}:
1
3
5
7
9
11
13
15
17
19
⋮
983
985
987
989
991
993
995
997
999
julia> df[!, :A] == df[:, :A]
true
В первом случае [:A]
является вектором, указывающим, что результирующий объект должен быть фреймом данных (DataFrame
). С другой стороны, :A
— это одиночный символ, указывающий на то, что должен быть извлечен вектор с одним столбцом. Обратите внимание, что в первом случае требуется передать вектор (а не просто итерируемый объект), поэтому, например, df[:, (:x1, :x2)]
не разрешается, а df[:, [:x1, :x2]]
допускается.
Также можно использовать регулярное выражение в качестве селектора столбцов, соответствующих ему:
julia> df = DataFrame(x1=1, x2=2, y=3)
1×3 DataFrame
Row │ x1 x2 y
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 2 3
julia> df[!, r"x"]
1×2 DataFrame
Row │ x1 x2
│ Int64 Int64
─────┼──────────────
1 │ 1 2
Селектор Not
(из пакета InvertedIndices) можно применять для выбора всех столбцов, за исключением определенного подмножества:
julia> df[!, Not(:x1)]
1×2 DataFrame
Row │ x2 y
│ Int64 Int64
─────┼──────────────
1 │ 2 3
Наконец, можно использовать селекторы Not
, Between
, Cols
и All
в более сложных сценариях выбора столбцов (обратите внимание, что Cols()
не выбирает ни одного столбца, а All()
выбирает все столбцы, поэтому Cols
является предпочтительным селектором при написании универсального кода). Ниже приведены примеры использования каждого из этих селекторов:
julia> df = DataFrame(r=1, x1=2, x2=3, y=4)
1×4 DataFrame
Row │ r x1 x2 y
│ Int64 Int64 Int64 Int64
─────┼────────────────────────────
1 │ 1 2 3 4
julia> df[:, Not(:r)] # удаление столбца :r
1×3 DataFrame
Row │ x1 x2 y
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 2 3 4
julia> df[:, Between(:r, :x2)] # сохранение столбцов между :r и :x2
1×3 DataFrame
Row │ r x1 x2
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 2 3
julia> df[:, All()] # сохранение всех столбцов
1×4 DataFrame
Row │ r x1 x2 y
│ Int64 Int64 Int64 Int64
─────┼────────────────────────────
1 │ 1 2 3 4
julia> df[:, Cols(x -> startswith(x, "x"))] # сохранение столбцов, имя которых начинается с "x"
1×2 DataFrame
Row │ x1 x2
│ Int64 Int64
─────┼──────────────
1 │ 2 3
В следующих примерах показано более сложное использование селектора Cols
, который перемещает все столбцы, имена которых соответствуют регулярному выражению r"x"
, соответственно в начало и в конец фрейма данных:
julia> df[:, Cols(r"x", :)]
1×4 DataFrame
Row │ x1 x2 r y
│ Int64 Int64 Int64 Int64
─────┼────────────────────────────
1 │ 2 3 1 4
julia> df[:, Cols(Not(r"x"), :)]
1×4 DataFrame
Row │ r y x1 x2
│ Int64 Int64 Int64 Int64
─────┼────────────────────────────
1 │ 1 4 2 3
Синтаксис индексирования также можно использовать для выбора строк на основе условий для переменных:
julia> df = DataFrame(A=1:2:1000, B=repeat(1:10, inner=50), C=1:500)
500×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 1 1
2 │ 3 1 2
3 │ 5 1 3
4 │ 7 1 4
5 │ 9 1 5
6 │ 11 1 6
7 │ 13 1 7
8 │ 15 1 8
⋮ │ ⋮ ⋮ ⋮
494 │ 987 10 494
495 │ 989 10 495
496 │ 991 10 496
497 │ 993 10 497
498 │ 995 10 498
499 │ 997 10 499
500 │ 999 10 500
485 rows omitted
julia> df[df.A .> 500, :]
250×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 501 6 251
2 │ 503 6 252
3 │ 505 6 253
4 │ 507 6 254
5 │ 509 6 255
6 │ 511 6 256
7 │ 513 6 257
8 │ 515 6 258
⋮ │ ⋮ ⋮ ⋮
244 │ 987 10 494
245 │ 989 10 495
246 │ 991 10 496
247 │ 993 10 497
248 │ 995 10 498
249 │ 997 10 499
250 │ 999 10 500
235 rows omitted
julia> df[(df.A .> 500) .& (300 .< df.C .< 400), :]
99×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 601 7 301
2 │ 603 7 302
3 │ 605 7 303
4 │ 607 7 304
5 │ 609 7 305
6 │ 611 7 306
7 │ 613 7 307
8 │ 615 7 308
⋮ │ ⋮ ⋮ ⋮
93 │ 785 8 393
94 │ 787 8 394
95 │ 789 8 395
96 │ 791 8 396
97 │ 793 8 397
98 │ 795 8 398
99 │ 797 8 399
84 rows omitted
Если необходимо сопоставить определенное подмножество значений, можно применить функцию in()
:
julia> df[in.(df.A, Ref([1, 5, 601])), :]
3×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 1 1
2 │ 5 1 3
3 │ 601 7 301
Оболочка Ref
для [1, 5, 601]
необходима для защиты вектора от трансляции (вектор будет рассматриваться как скаляр, если он заключен в Ref
). Вы можете написать эту операцию, используя следующее включение (обратите внимание, что она будет медленнее, поэтому не рекомендуется): [a in [1, 5, 601] for a in df.A]
.
Также функция in
может быть вызвана с одним аргументом для создания объекта функции, который проверяет, принадлежит ли каждое значение подмножеству (частичное применение in
): df[in([1, 5, 601]).(df.A), :]
.
Как и в случае с матрицами, подмножество из фрейма данных обычно возвращает копию столбцов, а не представление или прямую ссылку. Единственными ситуациями индексирования, когда фреймы данных не возвращают копию, являются следующие:
Более подробную информацию о копиях, видах и ссылках можно найти в разделе [ |
Функции выделения подмножеств
Альтернативным подходом к выделению подмножества строк во фрейме данных является использование функции subset
или функции subset!
, которая является ее вариантом на месте.
Эти функции принимают в качестве первого аргумента фрейм данных. Следующие позиционные аргументы (один или несколько) представляют собой спецификации условий фильтрации, которые должны быть выполнены совместно. Каждое условие должно передаваться в виде пары (Pair
), состоящей из исходного столбца (столбцов) и функции, задающей условие фильтрации, принимающей в качестве аргументов этот или эти столбцы (столбцы):
julia> subset(df, :A => a -> a .< 10, :C => c -> isodd.(c))
3×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 1 1
2 │ 5 1 3
3 │ 9 1 5
Часто случается, что в столбцах фильтрации могут присутствовать отсутствующие (missing
) значения, что может привести к тому, что условие фильтрации вернет missing
вместо ожидаемых true
или false
. Чтобы справиться с этой ситуацией, можно либо использовать функцию coalesce
, либо передать функции subset
именованный аргумент skipmissing=true
. Вот пример:
julia> df = DataFrame(x=[1, 2, missing, 4])
4×1 DataFrame
Row │ x
│ Int64?
─────┼─────────
1 │ 1
2 │ 2
3 │ missing
4 │ 4
julia> subset(df, :x => x -> coalesce.(iseven.(x), false))
2×1 DataFrame
Row │ x
│ Int64?
─────┼────────
1 │ 2
2 │ 4
julia> subset(df, :x => x -> iseven.(x), skipmissing=true)
2×1 DataFrame
Row │ x
│ Int64?
─────┼────────
1 │ 2
2 │ 4
Функция subset
была разработана так, чтобы соответствовать способу задания преобразований столбцов в таких функциях, как combine
, select
и transform
. Примеры преобразований столбцов, принимаемых этими функциями, приведены в следующем разделе.
Кроме того, DataFrames.jl расширяет функции filter
и filter!
, доступные в Julia Base, которые также позволяют выделять подмножество во фрейме данных. Эти методы определены таким образом, чтобы пакет DataFrames.jl реализовывал API Julia для коллекций, но обычно рекомендуется использовать функции subset
и subset!
, поскольку они соответствуют другим функциям DataFrames.jl (в отличие от filter
и filter!
).
Выбор и преобразование столбцов
Вы также можете использовать функции select
/select!
и transform
/transform!
для выбора, переименования и преобразования столбцов во фрейме данных.
Функция select
создает новый фрейм данных:
julia> df = DataFrame(x1=[1, 2], x2=[3, 4], y=[5, 6])
2×3 DataFrame
Row │ x1 x2 y
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 3 5
2 │ 2 4 6
julia> select(df, Not(:x1)) # удаление столбца :x1 в новом фрейме данных
2×2 DataFrame
Row │ x2 y
│ Int64 Int64
─────┼──────────────
1 │ 3 5
2 │ 4 6
julia> select(df, r"x") # выбор столбцов, содержащие символ 'x'
2×2 DataFrame
Row │ x1 x2
│ Int64 Int64
─────┼──────────────
1 │ 1 3
2 │ 2 4
julia> select(df, :x1 => :a1, :x2 => :a2) # переименование столбцов
2×2 DataFrame
Row │ a1 a2
│ Int64 Int64
─────┼──────────────
1 │ 1 3
2 │ 2 4
julia> select(df, :x1, :x2 => (x -> x .- minimum(x)) => :x2) # преобразование столбцов
2×2 DataFrame
Row │ x1 x2
│ Int64 Int64
─────┼──────────────
1 │ 1 0
2 │ 2 1
julia> select(df, :x2, :x2 => ByRow(sqrt)) # преобразование столбцов построчно
2×2 DataFrame
Row │ x2 x2_sqrt
│ Int64 Float64
─────┼────────────────
1 │ 3 1.73205
2 │ 4 2.0
julia> select(df, :x1, :x2, [:x1, :x2] => ((x1, x2) -> x1 ./ x2) => :z) # преобразование нескольких столбцов
2×3 DataFrame
Row │ x1 x2 z
│ Int64 Int64 Float64
─────┼────────────────────────
1 │ 1 3 0.333333
2 │ 2 4 0.5
julia> select(df, :x1, :x2, [:x1, :x2] => ByRow((x1, x2) -> x1 / x2) => :z) # преобразование нескольких столбцов построчно
2×3 DataFrame
Row │ x1 x2 z
│ Int64 Int64 Float64
─────┼────────────────────────
1 │ 1 3 0.333333
2 │ 2 4 0.5
julia> select(df, AsTable(:) => ByRow(extrema) => [:lo, :hi]) # возврат нескольких столбцов
2×2 DataFrame
Row │ lo hi
│ Int64 Int64
─────┼──────────────
1 │ 1 5
2 │ 2 6
Важно отметить, что select
всегда возвращает фрейм данных, даже если выбран один столбец (в отличие от синтаксиса индексирования).
julia> select(df, :x1)
2×1 DataFrame
Row │ x1
│ Int64
─────┼───────
1 │ 1
2 │ 2
julia> df[:, :x1]
2-element Vector{Int64}:
1
2
По умолчанию select
копирует столбцы переданного исходного фрейма данных. Чтобы копирование не выполнялось, передайте copycols=false
:
julia> df2 = select(df, :x1) 2×1 DataFrame Row │ x1 │ Int64 ─────┼─────── 1 │ 1 2 │ 2 julia> df2.x1 === df.x1 false julia> df2 = select(df, :x1, copycols=false) 2×1 DataFrame Row │ x1 │ Int64 ─────┼─────── 1 │ 1 2 │ 2 julia> df2.x1 === df.x1 true
Чтобы выполнить операцию выбора на месте, используйте select!
:
julia> select!(df, Not(:x1));
julia> df
2×2 DataFrame
Row │ x2 y
│ Int64 Int64
─────┼──────────────
1 │ 3 5
2 │ 4 6
Функции transform
и transform!
работают так же, как select
и select!
, с той лишь разницей, что они сохраняют все столбцы, которые присутствуют в исходном фрейме данных. Вот ряд более сложных примеров.
Сначала покажем, как сгенерировать столбец, который является суммой всех остальных столбцов во фрейме данных, используя селектор All()
:
julia> df = DataFrame(x1=[1, 2], x2=[3, 4], y=[5, 6])
2×3 DataFrame
Row │ x1 x2 y
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 3 5
2 │ 2 4 6
julia> transform(df, All() => +)
2×4 DataFrame
Row │ x1 x2 y x1_x2_y_+
│ Int64 Int64 Int64 Int64
─────┼────────────────────────────────
1 │ 1 3 5 9
2 │ 2 4 6 12
С помощью оболочки ByRow
можно легко вычислить для каждой строки название столбца с наибольшим количеством баллов:
julia> using Random julia> Random.seed!(1); julia> df = DataFrame(rand(10, 3), [:a, :b, :c]) 10×3 DataFrame Row │ a b c │ Float64 Float64 Float64 ─────┼────────────────────────────────── 1 │ 0.236033 0.555751 0.0769509 2 │ 0.346517 0.437108 0.640396 3 │ 0.312707 0.424718 0.873544 4 │ 0.00790928 0.773223 0.278582 5 │ 0.488613 0.28119 0.751313 6 │ 0.210968 0.209472 0.644883 7 │ 0.951916 0.251379 0.0778264 8 │ 0.999905 0.0203749 0.848185 9 │ 0.251662 0.287702 0.0856352 10 │ 0.986666 0.859512 0.553206 julia> transform(df, AsTable(:) => ByRow(argmax) => :prediction) 10×4 DataFrame Row │ a b c prediction │ Float64 Float64 Float64 Symbol ─────┼────────────────────────────────────────────── 1 │ 0.236033 0.555751 0.0769509 b 2 │ 0.346517 0.437108 0.640396 c 3 │ 0.312707 0.424718 0.873544 c 4 │ 0.00790928 0.773223 0.278582 b 5 │ 0.488613 0.28119 0.751313 c 6 │ 0.210968 0.209472 0.644883 c 7 │ 0.951916 0.251379 0.0778264 a 8 │ 0.999905 0.0203749 0.848185 a 9 │ 0.251662 0.287702 0.0856352 b 10 │ 0.986666 0.859512 0.553206 a
В самом сложном примере ниже мы вычисляем сумму построчно, количество элементов и среднее значение, игнорируя пропущенные значения.
julia> using Statistics julia> df = DataFrame(x=[1, 2, missing], y=[1, missing, missing]) 3×2 DataFrame Row │ x y │ Int64? Int64? ─────┼────────────────── 1 │ 1 1 2 │ 2 missing 3 │ missing missing julia> transform(df, AsTable(:) .=> ByRow.([sum∘skipmissing, x -> count(!ismissing, x), mean∘skipmissing]) .=> [:sum, :n, :mean]) 3×5 DataFrame Row │ x y sum n mean │ Int64? Int64? Int64 Int64 Float64 ─────┼───────────────────────────────────────── 1 │ 1 1 2 2 1.0 2 │ 2 missing 2 1 2.0 3 │ missing missing 0 0 NaN
Хотя пакет DataFrames.jl предоставляет базовые возможности работы с данными, пользователям рекомендуется использовать платформы запросов для выполнения более удобных и мощных операций:
-
пакет Query.jl предоставляет LINQ-подобный интерфейс для большого количества источников данных;
-
пакет DataFramesMeta.jl предоставляет интерфейсы, аналогичные LINQ и dplyr;
-
пакет DataFrameMacros.jl предоставляет макросы для большинства стандартных функций из DataFrames.jl с удобным синтаксисом для операций сразу с несколькими столбцами.
Дополнительные сведения см. в разделе Data manipulation frameworks.
Обобщение данных
Функция describe
возвращает фрейм данных, обобщающий элементарную статистику и информацию о каждом столбце:
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> describe(df)
2×7 DataFrame
Row │ variable mean min median max nmissing eltype
│ Symbol Union… Any Union… Any Int64 DataType
─────┼────────────────────────────────────────────────────────
1 │ A 2.5 1 2.5 4 0 Int64
2 │ B F M 0 String
Если вас интересует описание только подмножества столбцов, проще всего сделать это, передав подмножество исходного фрейма данных в describe
следующим образом.
julia> describe(df[!, [:A]])
1×7 DataFrame
Row │ variable mean min median max nmissing eltype
│ Symbol Float64 Int64 Float64 Int64 Int64 DataType
─────┼──────────────────────────────────────────────────────────────
1 │ A 2.5 1 2.5 4 0 Int64
Конечно, можно также вычислять описательную статистику непосредственно по отдельным столбцам:
julia> using Statistics
julia> mean(df.A)
2.5
Мы также можем применить функцию к каждому столбцу DataFrame
, используя combine
. Пример:
julia> df = DataFrame(A=1:4, B=4.0:-1.0:1.0)
4×2 DataFrame
Row │ A B
│ Int64 Float64
─────┼────────────────
1 │ 1 4.0
2 │ 2 3.0
3 │ 3 2.0
4 │ 4 1.0
julia> combine(df, names(df) .=> sum)
1×2 DataFrame
Row │ A_sum B_sum
│ Int64 Float64
─────┼────────────────
1 │ 10 10.0
julia> combine(df, names(df) .=> sum, names(df) .=> prod)
1×4 DataFrame
Row │ A_sum B_sum A_prod B_prod
│ Int64 Float64 Int64 Float64
─────┼─────────────────────────────────
1 │ 10 10.0 24 24.0
Если вы хотите, чтобы результат содержал то же количество строк, что и исходный фрейм данных, используйте select
вместо combine
.
Работа со столбцами, хранящимися в DataFrame
Функции, преобразующие DataFrame
для получения нового DataFrame
, по умолчанию всегда выполняют копирование столбцов, например:
julia> df = DataFrame(A=1:4, B=4.0:-1.0:1.0)
4×2 DataFrame
Row │ A B
│ Int64 Float64
─────┼────────────────
1 │ 1 4.0
2 │ 2 3.0
3 │ 3 2.0
4 │ 4 1.0
julia> df2 = copy(df);
julia> df2.A === df.A
false
С другой стороны, функции на месте, имена которых заканчиваются на !
, могут изменять векторы столбцов DataFrame
, которые они принимают в качестве аргумента. Пример:
julia> x = [3, 1, 2];
julia> df = DataFrame(x=x)
3×1 DataFrame
Row │ x
│ Int64
─────┼───────
1 │ 3
2 │ 1
3 │ 2
julia> sort!(df)
3×1 DataFrame
Row │ x
│ Int64
─────┼───────
1 │ 1
2 │ 2
3 │ 3
julia> x
3-element Vector{Int64}:
3
1
2
julia> df.x[1] = 100
100
julia> df
3×1 DataFrame
Row │ x
│ Int64
─────┼───────
1 │ 100
2 │ 2
3 │ 3
julia> x
3-element Vector{Int64}:
3
1
2
Обратите внимание, что в приведенном выше примере исходный вектор x
не изменяется в процессе, поскольку конструктор DataFrame(x=x)
по умолчанию создает его копию.
Функции на месте можно без проблем вызывать, за исключением случаев, когда используется представление DataFrame
(созданное с помощью view
, @view
или groupby
) или DataFrame
, созданный с помощью copycols=false
.
Прямой доступ к столбцу col
DataFrame
df
можно получить, используя синтаксисы df.col
, df[!, :col]
, с помощью функции eachcol
, обращаясь к parent
view
столбца DataFrame
, или просто сохранив ссылку на вектор столбцов до создания DataFrame
с помощью copycols=false
.
julia> x = [3, 1, 2];
julia> df = DataFrame(x=x)
3×1 DataFrame
Row │ x
│ Int64
─────┼───────
1 │ 3
2 │ 1
3 │ 2
julia> df.x == x
true
julia> df[!, 1] !== x
true
julia> eachcol(df)[1] === df.x
true
Обратите внимание, что столбец, полученный из DataFrame
с помощью одного из этих методов, следует изменять с особой осторожностью.
Точные правила работы со столбцами DataFrame
описаны в разделе Принципы работы со столбцами DataFrame
руководства.
Замена данных
Существует несколько подходов к замене одних значений другими во фрейме данных. Одни применяют замену ко всем значениям во фрейме данных, другие — к отдельным столбцам или подмножествам столбцов.
Обратите внимание, что для замены на месте требуется, чтобы заменяемое значение можно было преобразовать в тип элемента столбца. В частности, это означает, что для замены значения на missing
требуется вызов allowmissing!
, если столбец запрещал отсутствующие значения.
Операции замены, затрагивающие один столбец, можно выполнять с помощью функции replace!
:
julia> using DataFrames
julia> df = DataFrame(a=["a", "None", "b", "None"], b=1:4,
c=["None", "j", "k", "h"], d=["x", "y", "None", "z"])
4×4 DataFrame
Row │ a b c d
│ String Int64 String String
─────┼───────────────────────────────
1 │ a 1 None x
2 │ None 2 j y
3 │ b 3 k None
4 │ None 4 h z
julia> replace!(df.a, "None" => "c")
4-element Vector{String}:
"a"
"c"
"b"
"c"
julia> df
4×4 DataFrame
Row │ a b c d
│ String Int64 String String
─────┼───────────────────────────────
1 │ a 1 None x
2 │ c 2 j y
3 │ b 3 k None
4 │ c 4 h z
Она эквивалентна df.a = replace(df.a, "None" => "c")
, но работает на месте без выделения нового вектора столбцов.
Операции замены в нескольких столбцах или во всем фрейме данных можно выполнять на месте, используя синтаксис трансляции:
# замена в подмножестве столбцов [:c, :d]
julia> df[:, [:c, :d]] .= ifelse.(df[!, [:c, :d]] .== "None", "c", df[!, [:c, :d]])
4×2 SubDataFrame
Row │ c d
│ String String
─────┼────────────────
1 │ c x
2 │ j y
3 │ k c
4 │ h z
julia> df
4×4 DataFrame
Row │ a b c d
│ String Int64 String String
─────┼───────────────────────────────
1 │ a 1 c x
2 │ c 2 j y
3 │ b 3 k c
4 │ c 4 h z
julia> df .= ifelse.(df .== "c", "None", df) # замена во всем фрейме данных
4×4 DataFrame
Row │ a b c d
│ String Int64 String String
─────┼───────────────────────────────
1 │ a 1 None x
2 │ None 2 j y
3 │ b 3 k None
4 │ None 4 h z
Обратите внимание, что в приведенных выше примерах замена .=
на =
приведет к выделению новых векторов столбцов, а не к применению операции на месте.
При замене значений на отсутствующие (missing
), если столбцы еще запрещают пропущенные значения, необходимо либо не выполнять операцию на месте и использовать =
вместо .=
, либо предварительно вызвать allowmissing!
:
julia> df2 = ifelse.(df .== "None", missing, df) # не выполнять на месте (`df = ` будет работать)
4×4 DataFrame
Row │ a b c d
│ String? Int64 String? String?
─────┼──────────────────────────────────
1 │ a 1 missing x
2 │ missing 2 j y
3 │ b 3 k missing
4 │ missing 4 h z
julia> allowmissing!(df) # выполнять на месте после разрешения отсутствующих значений
4×4 DataFrame
Row │ a b c d
│ String? Int64? String? String?
─────┼───────────────────────────────────
1 │ a 1 None x
2 │ None 2 j y
3 │ b 3 k None
4 │ None 4 h z
julia> df .= ifelse.(df .== "None", missing, df)
4×4 DataFrame
Row │ a b c d
│ String? Int64? String? String?
─────┼───────────────────────────────────
1 │ a 1 missing x
2 │ missing 2 j y
3 │ b 3 k missing
4 │ missing 4 h z