Фреймы данных : нет метода, соответствующего setindex!(::Фрейм данных, ::Кортеж{Float64, Float64}, ::Двоеточие, ::Строка)

#dataframe #julia

Вопрос:

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

Вот игрушечный пример,

 df = DataFrame()

df[:, :x] = rand(5)
df[:, :y] = rand(5)

#Function that returns two values in the form of a tuple
add_minus_two(x,y) = (x-y,x y)

df[:,"x y"] = add_minus_two.(df[:,:x], df[:,:y])[2]
#Out > ERROR: MethodError: no method matching setindex!(::DataFrame, ::Tuple{Float64, Float64}, ::Colon, ::String)

#However removing the dot operator works fine
df[:,"x y"] = add_minus_two(df[:,:x], df[:,:y])[2]
#Out > 5 x 3 DataFrame

#Furthermore if its just one argument either dot or not, works fine as well
add_two(x,y) = x y
df[:, "x y"] = add_two(df[:,:x], df[:,:y])
df[:, "x y"] = add_two.(df[:,:x], df[:,:y])
#out > 5 x 3 DataFrame
 

Любая причина, по которой это так. Я думал, что для поэлементной операции вам нужно использовать оператор «точка».

Также для моей реальной проблемы (когда функция возвращает 2 значения в кортеже), когда НЕ используется оператор точки, дает,

  ERROR: MethodError: no method matching compute_T(::Vector{Float64}, ::Vector{Float64})
 

и использование оператора точки дает,

 ERROR: MethodError: no method matching setindex!(::DataFrame, ::Tuple{Float64, Float64}, ::Colon, ::String)  
 

и возврат одного аргумента, аналогичного примеру с игрушкой, также отлично работает.

Есть какие-нибудь подсказки, что я здесь делаю неправильно ?

Ответ №1:

Это не проблема DataFrames.jl, а то, как работает база Julia.

Я концентрируюсь только на RHS, так как LHS не имеет значения (а RHS не имеет отношения к фреймам данных.jl).

Во-первых, как написать то, что вы хотите. Инициализация:

 julia> using DataFrames

julia> df = DataFrame()
0×0 DataFrame

julia> df[:, :x] = rand(5)
5-element Vector{Float64}:
 0.6146045473316457
 0.6319531776216596
 0.599267794937812
 0.40864382019544965
 0.3738682778395166

julia> df[:, :y] = rand(5)
5-element Vector{Float64}:
 0.07891853567296825
 0.2143545316544586
 0.5943274462916335
 0.2182702556068421
 0.5810132720450707

julia> add_minus_two(x,y) = (x-y,x y)
add_minus_two (generic function with 1 method)
 

И теперь вы получаете:

 julia> add_minus_two(df[:,:x], df[:,:y])
([0.5356860116586775, 0.417598645967201, 0.004940348646178538, 0.19037356458860755, -0.2071449942055541], [0.693523083004614, 0.8463077092761182, 1.1935952412294455, 0.6269140758022917, 0.9548815498845873])

julia> add_minus_two.(df[:,:x], df[:,:y])
5-element Vector{Tuple{Float64, Float64}}:
 (0.5356860116586775, 0.693523083004614)
 (0.417598645967201, 0.8463077092761182)
 (0.004940348646178538, 1.1935952412294455)
 (0.19037356458860755, 0.6269140758022917)
 (-0.2071449942055541, 0.9548815498845873)

julia> add_minus_two(df[:,:x], df[:,:y])[2]
5-element Vector{Float64}:
 0.693523083004614
 0.8463077092761182
 1.1935952412294455
 0.6269140758022917
 0.9548815498845873

julia> add_minus_two.(df[:,:x], df[:,:y])[2]
(0.417598645967201, 0.8463077092761182)

julia> getindex.(add_minus_two.(df[:,:x], df[:,:y]), 2) # this is probably what you want
5-element Vector{Float64}:
 0.693523083004614
 0.8463077092761182
 1.1935952412294455
 0.6269140758022917
 0.9548815498845873
 

Теперь дело в том, что когда вы пишете:

 df[:,"x y"] = whatever_you_pass
 

whatever_you_pass Деталь должна быть AbstractVector с соответствующим количеством столбцов. Это означает, что то, что будет работать, — это:

  • add_minus_two.(df[:,:x], df[:,:y])
  • add_minus_two(df[:,:x], df[:,:y])[2]
  • getindex.(add_minus_two.(df[:,:x], df[:,:y]), 2)

и что потерпит неудачу (так как в этих случаях Tuple не AbstractVector производится)

  • add_minus_two(df[:,:x], df[:,:y])
  • add_minus_two.(df[:,:x], df[:,:y])[2]

Из рабочих синтаксисов просто выберите тот, который вам нужен.

Общая рекомендация заключается в том, что при выполнении задания всегда проверяйте RHS отдельно и анализируйте, имеет ли он надлежащую структуру.

Кроме того, примечательно, что это будет работать:

 julia> transform(df, [:x, :y] => ByRow(add_minus_two) => ["x-y", "x y"])
5×4 DataFrame
 Row │ x         y          x-y          x y
     │ Float64   Float64    Float64      Float64
─────┼────────────────────────────────────────────
   1 │ 0.614605  0.0789185   0.535686    0.693523
   2 │ 0.631953  0.214355    0.417599    0.846308
   3 │ 0.599268  0.594327    0.00494035  1.1936
   4 │ 0.408644  0.21827     0.190374    0.626914
   5 │ 0.373868  0.581013   -0.207145    0.954882
 

(вы не спрашивали об этом, но, возможно, это то, что вы на самом деле ищете — и, в отличие от setindex! этого синтаксиса, специфичен для DataFrames.jl)