Есть ли способ вычесть сразу несколько столбцов фрейма данных?

#julia

#джулия

Вопрос:

Я довольно новичок в Джулии, поэтому прошу прощения, если это очень простой вопрос. Из R я привык выполнять базовые операции с несколькими столбцами фрейма данных одновременно. Я попытался сделать это в Julia следующим образом:

У меня есть два фрейма данных, давайте назовем их data_1 и data_2:

 using DataFrames
data_1 = DataFrame(rand(4,6))
data_2 = DataFrame(zeros(4,6))
  

И теперь я хочу заполнить data_2 как разницу определенных строк из data_1, например:

 data_2[1,:] = data_1[1,:] - data_1[2,:]
  

но это приводит к ошибке. Итак, как я могу изменить этот подход, чтобы успешно вычесть несколько столбцов строк фрейма данных?

Большое вам спасибо!

Ответ №1:

К сожалению, эта конкретная задача немного сложна, поскольку мы пытаемся сохранить согласованность с базой Julia.

Вот способы сделать это:

Вариант 1

Используйте итерацию:

 julia> data_1 = DataFrame(reshape(1:24, 4, 6))
4×6 DataFrame
│ Row │ x1    │ x2    │ x3    │ x4    │ x5    │ x6    │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Int64 │ Int64 │
├─────┼───────┼───────┼───────┼───────┼───────┼───────┤
│ 1   │ 1     │ 5     │ 9     │ 13    │ 17    │ 21    │
│ 2   │ 2     │ 6     │ 10    │ 14    │ 18    │ 22    │
│ 3   │ 3     │ 7     │ 11    │ 15    │ 19    │ 23    │
│ 4   │ 4     │ 8     │ 12    │ 16    │ 20    │ 24    │

julia> data_2 = DataFrame(zeros(4,6))
4×6 DataFrame
│ Row │ x1      │ x2      │ x3      │ x4      │ x5      │ x6      │
│     │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ 1   │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │
│ 2   │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │
│ 3   │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │
│ 4   │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │

julia> foreach(i -> data_2[1,i] = data_1[1, i] - data_1[2, i], axes(data_1, 2))

julia> data_2
4×6 DataFrame
│ Row │ x1      │ x2      │ x3      │ x4      │ x5      │ x6      │
│     │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ 1   │ -1.0    │ -1.0    │ -1.0    │ -1.0    │ -1.0    │ -1.0    │
│ 2   │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │
│ 3   │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │
│ 4   │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │ 0.0     │
  

Вариант 2

Используйте широковещательную передачу фреймов данных:

 julia> data_1 = DataFrame(reshape(1:24, 4, 6))
4×6 DataFrame
│ Row │ x1    │ x2    │ x3    │ x4    │ x5    │ x6    │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Int64 │ Int64 │
├─────┼───────┼───────┼───────┼───────┼───────┼───────┤
│ 1159131721    │
│ 22610141822    │
│ 33711151923    │
│ 44812162024    │

julia> data_2
4×6 DataFrame
│ Row │ x1      │ x2      │ x3      │ x4      │ x5      │ x6      │
│     │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ 1-1.0-1.0-1.0-1.0-1.0-1.0    │
│ 20.00.00.00.00.00.0     │
│ 30.00.00.00.00.00.0     │
│ 40.00.00.00.00.00.0     │

julia> data_2[1:1,:] .= data_1[1:1,:] .- data_1[2:2,:]
1×6 SubDataFrame
│ Row │ x1      │ x2      │ x3      │ x4      │ x5      │ x6      │
│     │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ 1-1.0-1.0-1.0-1.0-1.0-1.0    │

julia> data_2
4×6 DataFrame
│ Row │ x1      │ x2      │ x3      │ x4      │ x5      │ x6      │
│     │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ 1-1.0-1.0-1.0-1.0-1.0-1.0    │
│ 20.00.00.00.00.00.0     │
│ 30.00.00.00.00.00.0     │
│ 40.00.00.00.00.00.0 

Как вы можете видеть, хитрость заключается в том, чтобы использовать широковещательные ( . ) и фрагменты ( 1:1 и т.д.), А не отдельные индексы.

Проблема с отдельными индексами заключается в том, что DataFrameRow сейчас не поддерживается трансляция:

 julia> data_2[1,:] .= data_1[1,:] .- data_1[2,:]
ERROR: ArgumentError: broadcasting over `DataFrameRow`s is reserved
  

поскольку не определено, как будет работать трансляция для NamedTuple объектов в Base, как вы можете видеть здесь:

 julia> (a=1,b=2) .- (a=1,b=2)
ERROR: ArgumentError: broadcasting over dictionaries and `NamedTuple`s is reserved
  

(как только база поддержит широковещательную передачу NamedTuples , мы добавим эту поддержку к DataFrameRow s)

Вариант 3

Это обходной путь к проблеме отсутствия трансляции DataFrameRow объекта:

 julia> data_1 = DataFrame(reshape(1:24, 4, 6))
4×6 DataFrame
│ Row │ x1    │ x2    │ x3    │ x4    │ x5    │ x6    │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Int64 │ Int64 │
├─────┼───────┼───────┼───────┼───────┼───────┼───────┤
│ 1159131721    │
│ 22610141822    │
│ 33711151923    │
│ 44812162024    │

julia> data_2 = DataFrame(zeros(4,6))
4×6 DataFrame
│ Row │ x1      │ x2      │ x3      │ x4      │ x5      │ x6      │
│     │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ 10.00.00.00.00.00.0     │
│ 20.00.00.00.00.00.0     │
│ 30.00.00.00.00.00.0     │
│ 40.00.00.00.00.00.0     │

julia> data_2[1,:] = Vector(data_1[1,:]) - Vector(data_1[2,:])
DataFrameRow
│ Row │ x1      │ x2      │ x3      │ x4      │ x5      │ x6      │
│     │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ 1-1.0-1.0-1.0-1.0-1.0-1.0    │

julia> data_2
4×6 DataFrame
│ Row │ x1      │ x2      │ x3      │ x4      │ x5      │ x6      │
│     │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ 1-1.0-1.0-1.0-1.0-1.0-1.0    │
│ 20.00.00.00.00.00.0     │
│ 30.00.00.00.00.00.0     │
│ 40.00.00.00.00.00.0 

как вы можете видеть, хитрость заключается в преобразовании RHS в Vector s, которые поддерживают - .

Наконец (в качестве дополнительной ссылки, которая может быть полезна в некоторых случаях) вы можете написать Vector(data_1[1,:]) - Vector(data_1[2,:]) короче так же, как:

 julia> -(Vector.((data_1[1,:],data_1[2,:]))...)
6-element Array{Int64,1}:
 -1
 -1
 -1
 -1
 -1
 -1
  

Комментарии:

1. Спасибо, БК, как обычно, ты везде и незаменим.