Как создать собственный итератор в Julia 1.0? - PullRequest
0 голосов
/ 17 октября 2018

У меня есть эта структура в Julia 1.0:

mutable struct Metadata
    id::Int64
    res_id::Int64
end

Так что я могу создать их массив, где id всегда увеличивается на единицу, но res_id только иногда увеличиваетсявот так:

data = [
    Metadata(1, 1),
    Metadata(2, 1),
    Metadata(3, 1),
    Metadata(4, 2),
    Metadata(5, 2),
    Metadata(6, 2),
...]

Что я хочу сделать, так это уметь перебирать этот массив, но получать блоки на основе res_id (все данные с res_id 1, затем 2 и т. д.).Желаемое поведение будет примерно таким:

for res in iter_res(data)
    println(res)
end

julia>
[Metadata(1, 1), Metadata(2, 1), Metadata(3, 1)]
[Metadata(4, 2), Metadata(5, 2), Metadata(6, 2)]

Как мне это сделать в Julia 1.0, учитывая, что мне также нужно нормально перебирать массив, чтобы получить элемент за элементом?

Ответы [ 4 ]

0 голосов
/ 18 октября 2018

Как я в конечном итоге справился с проблемой:

function iter(data::Vector{Metadata}; property::Symbol = :res_id)

    #GET UNIQUE VALUES FOR THIS PROPERTY
    up = Vector{Any}()
    for s in data
        getproperty(s, property) in up ? nothing : push!(up, getproperty(s, property))
    end

    #GROUP ELEMENTS BASED ON THE UNIQUE VALUES FOR THIS PROPERTY
    f = Vector{Vector{Metadata}}()
    idx::Int64 = 1
    cmp::Any = up[idx]
    push!(f, Vector{Metadata}())
    for s in data
        if getproperty(s, property) == cmp
            push!(f[idx], s)
        else
            push!(f, Vector{Metadata}())
            idx += 1
            cmp = up[idx]
            push!(f[idx], s)
        end
    end
    return f
end

Это позволяет мне учесть «пропущенные» res_id (например, переход с 1 на 3 и т. Д.) И даже сгруппировать объекты метаданных по другим будущим характеристикам, кромеres_id, например, Strings или типы, отличные от Int64.Работает, хотя, вероятно, это не очень эффективно.

Затем вы можете перебрать вектор {метаданные} следующим образом:

for r in iter(rs)
    println(res)
end
0 голосов
/ 17 октября 2018

Похоже, вам нужна функция groupBy.Вот инструмент для справки, в Haskell

groupBy                 :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _  []           =  []
groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
                           where (ys,zs) = span (eq x) xs
0 голосов
/ 17 октября 2018

По именам ваших переменных кажется, что вы собираете данные какого-то вычислительного процесса.Обычно вы используете DataFrame для этой цели.

using DataFrames
data = DataFrame(id=[1,2,3,4,5,6],res_id=[1,1,1,2,2,2])
for group in groupby(data,:res_id)
    println(group)
end

Это дает:

3×2 SubDataFrame{Array{Int64,1}}
│ Row │ id    │ res_id │
│     │ Int64 │ Int64  │
├─────┼───────┼────────┤
│ 1   │ 1     │ 1      │
│ 2   │ 2     │ 1      │
│ 3   │ 3     │ 1      │
3×2 SubDataFrame{Array{Int64,1}}
│ Row │ id    │ res_id │
│     │ Int64 │ Int64  │
├─────┼───────┼────────┤
│ 1   │ 4     │ 2      │
│ 2   │ 5     │ 2      │
│ 3   │ 6     │ 2      │

Это также более удобно для дальнейшей обработки результатов.

0 голосов
/ 17 октября 2018

Вы можете выполнить итерацию по Генератору из , фильтру с, как это:

julia> mutable struct Metadata
           id::Int64
           res_id::Int64
       end

julia> data = [
           Metadata(1, 1),
           Metadata(2, 1),
           Metadata(3, 1),
           Metadata(4, 2),
           Metadata(5, 2),
           Metadata(6, 2),
       ];

julia> for res in (filter(x -> x.res_id == i, data) for i in 1:2)
           println(res)
       end
Metadata[Metadata(1, 1), Metadata(2, 1), Metadata(3, 1)]
Metadata[Metadata(4, 2), Metadata(5, 2), Metadata(6, 2)]
...