Iterator 如何在Julia 1.0中创建自定义迭代器?
我在Julia 1.0中有这样的结构:Iterator 如何在Julia 1.0中创建自定义迭代器?,iterator,julia,Iterator,Julia,我在Julia 1.0中有这样的结构: mutable struct Metadata id::Int64 res_id::Int64 end 这样我就可以创建一个数组,其中id总是递增1,但是res\u id只是有时递增,如下所示: data = [ Metadata(1, 1), Metadata(2, 1), Metadata(3, 1), Metadata(4, 2), Metadata(5, 2), Metadata(6
mutable struct Metadata
id::Int64
res_id::Int64
end
这样我就可以创建一个数组,其中id
总是递增1,但是res\u id
只是有时递增,如下所示:
data = [
Metadata(1, 1),
Metadata(2, 1),
Metadata(3, 1),
Metadata(4, 2),
Metadata(5, 2),
Metadata(6, 2),
...]
我想做的是能够迭代这个数组,但是基于res\u id
获取块(所有数据都是res\u 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> 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)]
在Julia 1.0中如何实现这一点,考虑到我通常也需要迭代数组以逐元素获取数据?您可以像这样迭代数组:
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> 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)]
听起来您需要一个
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
从变量的名称来看,您似乎是在从某个计算过程中收集数据。通常情况下,您使用
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 │
这对于进一步处理结果也更方便。我最终是如何处理这个问题的:
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
这使我能够适应“跳过的”resu id(如从1跳到3等),甚至可以根据resu id以外的其他未来特征(如字符串或Int64以外的类型)对元数据对象进行分组。虽然它可能不是很有效率,但它很有效
然后可以通过以下方式迭代向量{Metadata}:
for r in iter(rs)
println(res)
end
在Julia 1+中,这应该通过实现
Base.iterate(::YourType)
来实现,以获得开始的迭代和Base.iterate(::YourType,state)
用于基于某些状态的其他迭代。完成后,这些方法应返回nothing
,否则,(结果、状态)
元组
使用
for i in x
# stuff
end
这就是写作的简写
it = iterate(x)
while it !== nothing
i, state = it
# stuff
it = iterate(x, state)
end
有关详细信息,请参见。但在这种情况下,我不知道只有两种类型的res_id可供选择,当您在1:2中为I说。如何找出数组中有多少种独特的res_id类型。如果他们有“差距”,比如1,3,4?谢谢,这似乎很有用。但是,我不知道将类型子数据帧
合并起来是否会缓解我的问题。不过,谢谢你的建议!您总是可以这样做:用于数据帧中的组。(groupby(data,:res_id))
然后您就有了一个数据帧
sy您应该得到这样的唯一值unique(getfield.(data,:res_id))
。永远不要在数组上使用完全扫描进行搜索(除非它只有3个或多个元素)。而是使用集合
,您的代码将无法与数据=[元数据(1,1)、元数据(2,3)、元数据(3,1)]
一起工作。您应该有一个单循环,并使用Dict{Int64,Vector{MetaData}
感谢您的反馈!我本想在另一个问题中发布这个答案,但我觉得它在这里仍然是相关的,所以我将把它留下。