Вещание, несколько n * m возвращается вместо элемента n * m [Julia 1.0] - PullRequest
0 голосов
/ 02 сентября 2018

Предположим, я хочу написать функцию, которая требует массивов любой длины в качестве входных аргументов: например.,

f = function(x,y) 
  z = x * y
  outputs = DataFrame(x = x, y = y, z = z)
  return(outputs)
end

Возвращаемое значение f.([1,2],[1,2]) - это двухэлементный массив из двух 1х3 фреймов данных. Но в этом сценарии я хочу один DataFrame 2x3.

Я мог бы добиться этого, определив z перед вложенным циклом for:

f = function(x,y)
  z=fill(0,length(x))
  for i in 1:length(x)
   z[i] = x[i] * y[i]
  end
  outputs = DataFrame(x = x, y = y, z = z)
  return(outputs)
end

Здесь f([1,2],[1, 2]) получает от меня то, что я хочу. Но проблема в том, что мне нужно дважды определить все переменные в функции и добавить цикл for, не забывая включить итеративную переменную i. Я что-то упускаю? У меня вопрос, как мне получить желаемый элемент n m, а не массив n m ... Я пытался следовать этому сообщению в блоге Джулии . Также в этом сообщении Юлии специально рассматривается проблема, но я думаю, что решения были устаревшими для 1.0.

---- РЕДАКТИРОВАТЬ

Использование цикла for может работать так же, как и использование точек для обозначения поэлементных операций. Большая проблема, о которой я беспокоюсь, это последовательность. Предположим, у меня есть две функции. Одна функция (f1) возвращает одномерный вывод, а другая (f2) - двумерный вывод.

 function f1(x, y)
  z = x .* y
  DataFrame(x = x, y = y, z = z)
 end
 function f2(x, y)
  z = x * y
  return(z)
 end

Здесь правильные вызовы, когда x = [1,2] и y = [1,2] будут f1([1,2], [1,2]) и f2.([1,2], [1,2]). То, что я называю здесь непоследовательным, заключается в том, что (с точки зрения пользователя, который не знает внутренний код функции), получить вывод, где z - это длина x и y, . используется с f2, но не f1. Единственный обходной путь, который я вижу, - это определить z = .x * y (или альтернативно использовать a для каждого цикла индекса) в f2. В этом случае оба f1 и f2 оба могут быть вызваны без точки. Это подходящее решение? Чтобы быть ясным, я стремлюсь к тому, чтобы f1 и f2 одинаково вызывались пользователем независимо от того, являются ли x и y массивами из одного или нескольких элементов. Я предпочел бы, чтобы пользователь вызывал обе функции без точки, если x и y - отдельные элементы, и с ., если каждая переменная имеет несколько элементов. Это не представляется возможным. Следовательно, часть, с которой я должен научиться жить - это написать много . или [i] в моих функциях (если я желаю «согласованности»). Правильно?

В качестве альтернативы, я мог бы добавить документацию, в которой явно указано, что мои функции, которые возвращают одну переменную, должны вызываться с ., когда аргументы имеют длину> 1, и функции, которые возвращают информационный кадр, не должны вызываться с . для любого причина. [простите любое неправильное использование технического языка; мой фон - экология]

Ответы [ 2 ]

0 голосов
/ 03 сентября 2018

Мои предпочтения должны состоять в том, чтобы пользователь вызывал обе функции без точки, если x и y являются единичными элементами и если .if, каждая переменная имеет несколько элементов.

Вам нужна функция, которая специализируется на типах аргументов. Самый элегантный и самый быстрый во время исполнения способ компиляции - это макрос @generated.

using DataFrames
@generated function f(a,b)
    if a<:Array && b<:Array
        code = quote
            DataFrame(x = a, y = b, z = a .* b)
        end
    else
        code = quote
            DataFrame(x = a, y = b, z = a * b)
        end
    end
    code
end

Теперь давайте проверим это. Обратите внимание, как поведение функции зависит от типов аргументов (Float64 против Int). Каждый из параметров может быть либо Array, либо скалярным.

julia> f(3,4)                    
1×3 DataFrame                    
│ Row │ x │ y │ z  │             
├─────┼───┼───┼────┤             
│ 1   │ 3 │ 4 │ 12 │             

julia> f(3,4.0)                  
1×3 DataFrame                    
│ Row │ x │ y   │ z    │         
├─────┼───┼─────┼──────┤         
│ 1   │ 3 │ 4.0 │ 12.0 │         

julia> f(3.0,[1,2,3])            
3×3 DataFrame                    
│ Row │ x   │ y │ z   │          
├─────┼─────┼───┼─────┤          
│ 1   │ 3.0 │ 1 │ 3.0 │          
│ 2   │ 3.0 │ 2 │ 6.0 │          
│ 3   │ 3.0 │ 3 │ 9.0 │    

julia> f([1,2,3],4)
3×3 DataFrame
│ Row │ x │ y │ z  │
├─────┼───┼───┼────┤
│ 1   │ 1 │ 4 │ 4  │
│ 2   │ 2 │ 4 │ 8  │
│ 3   │ 3 │ 4 │ 12 │      

julia> f([6,7,8],[1,2,3])        
3×3 DataFrame                    
│ Row │ x │ y │ z  │             
├─────┼───┼───┼────┤             
│ 1   │ 6 │ 1 │ 6  │             
│ 2   │ 7 │ 2 │ 14 │             
│ 3   │ 8 │ 3 │ 24 │             
0 голосов
/ 02 сентября 2018

Это то, что вы хотите?

julia> function f(x, y)
           z = x .* y
           DataFrame(x = x, y = y, z = z)
       end
f (generic function with 1 method)

julia> f([1,2], [1,2])
2×3 DataFrame
│ Row │ x │ y │ z │
├─────┼───┼───┼───┤
│ 1   │ 1 │ 1 │ 1 │
│ 2   │ 2 │ 2 │ 4 │

Вы также можете написать f(x, y) = DataFrame(x = x, y = y, z = x .* y) вкратце.

То, как вы написали определение функции, предполагает, что вы знаете R. В Julia, в отличие от R, скаляры и массивы являются полностью разделенными типами (например, Float64 и Vector{Float64}) и должны обрабатываться по-разному; но обычно просто добавление достаточного количества трансляций в нужных местах (а трансляция работает путем установки . после любого вызова функции или перед любым оператором).

Чтобы не смешивать такие вещи, вы можете добавить к аргументам типы: f(x::Vector{Float64}, y::Vector{Float64}) или все, что вам подходит.

...