Сохранение OrderedDict в формате данных Julia - PullRequest
3 голосов
/ 05 ноября 2019

Я хочу использовать пакет JLD для записи OrderedDict в файл таким образом, чтобы впоследствии я мог читать его без изменений.

Вот моя первая попытка:

using JLD, HDF5, DataStructures

function testjld()  
    res = OrderedDict("A" => 1, "B" => 2)
    filename = "c:/temp/test.jld"
    save(File(format"JLD", filename), "res", res)
    res2 = load(filename)["res"]
    #Check if round-tripping works
    res == res2
end

Но «круговое отключение» не работает - функция возвращает false. Это также вызывает предупреждение:

julia> testjld()
┌ Warning: type JLD.AssociativeWrapper{Core.String,Core.Int64,OrderedCollections.OrderedDict{Core.String,Core.Int64}} not present in workspace; reconstructing
└ @ JLD C:\Users\Philip\.julia\packages\JLD\1BoSz\src\jld_types.jl:703
false

После прочтения документации я подумал, что JLD не поддерживает OrderedDict "из коробки", но поддерживает Dict, и я могу использовать этоФакт, чтобы написать мою собственную пользовательскую сериализацию для OrderedDict. Примерно так:

struct OrderedDictSerializer
    d::Dict
end

JLD.writeas(data::OrderedDict) = OrderedDictSerializer(Dict("contents" => convert(Dict, data), 
                                                            "keyorder" => [k for (k, v) in data]))

function JLD.readas(serdata::OrderedDictSerializer)
    unordered = serdata.d["contents"]
    keyorder = serdata.d["keyorder"]
    OrderedDict((k, unordered[k]) for k in keyorder)
end

Едва исчерпывающий тест, но кажется, что он работает:

julia> testjld()
true

Правильно ли я считаю, что мне нужно написать собственный сериализатор для OrderedDictи можно ли улучшить мой сериализатор?

EDIT

Ответ на мой вопрос "Можно ли улучшить мой сериализатор?"Похоже, что «так и должно быть, хотя я пока не понимаю, как».

Рассмотрим две следующие тестовые функции:

function testjld2()
    res = OrderedDict("A" => [1.0,2.0],"B" => [3.0,4.0])
    #check if round-tripping of readas and writeas methods works:
    JLD.readas(JLD.writeas(res)) == res
end

function testjld3()
    res = OrderedDict("A" => [1.0,2.0],"B" => [3.0,4.0])
    filename = "c:/temp/test.jld"
    save(File(format"JLD", filename), "res", res)
    res2 = load(filename)["res"]
    #Check if round-tripping to jld file and back works
    res == res2
end

testjld2 показывает, что мой writeas и readas правильные методы в обоих направлениях для OrderedDict{String,Array{Float64,1}} with 2 entries

julia> testjld2()
true

и все же testjld3 не работает вообще, но выдает ошибку:

julia> testjld3()
HDF5-DIAG: Error detected in HDF5 (1.10.5) thread 0:
  #000: E:/mingwbuild/mingw-w64-hdf5/src/hdf5-1.10.5/src/H5Tfields.c line 60 in H5Tget_nmembers(): not a datatype
    major: Invalid arguments to routine
    minor: Inappropriate type
HDF5-DIAG: Error detected in HDF5 (1.10.5) thread 0:
  #000: E:/mingwbuild/mingw-w64-hdf5/src/hdf5-1.10.5/src/H5Tfields.c line 60 in H5Tget_nmembers(): not a datatype
    major: Invalid arguments to routine
    minor: Inappropriate type
ERROR: Error getting the number of members
Stacktrace:
 [1] error(::String) at .\error.jl:33
 [2] h5t_get_nmembers at C:\Users\Philip\.julia\packages\HDF5\rF1Fe\src\HDF5.jl:2279 [inlined]
 [3] _gen_h5convert!(::Any) at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\jld_types.jl:638
 [4] #s27#9(::Any, ::Any, ::Any, ::Any, ::Any, ::Any) at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\jld_types.jl:664
 [5] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any,N} where N) at .\boot.jl:524
 [6] #write_compound#24(::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::typeof(JLD.write_compound), ::JLD.JldGroup, ::String, ::JLD.AssociativeWrapper{String,Any,Dict{String,Any}}, ::JLD.JldWriteSession) at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:700
 [7] write_compound at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:694 [inlined]
 [8] #_write#23 at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:690 [inlined]
 [9] _write at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:690 [inlined]
 [10] write_ref(::JLD.JldFile, ::Dict{String,Any}, ::JLD.JldWriteSession) at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:658
 [11] macro expansion at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\jld_types.jl:648 [inlined]
 [12] h5convert!(::Ptr{UInt8}, ::JLD.JldFile, ::OrderedDictSerializer, ::JLD.JldWriteSession) at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\jld_types.jl:664
 [13] #write_compound#24(::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::typeof(JLD.write_compound), ::JLD.JldFile, ::String, ::OrderedDictSerializer, ::JLD.JldWriteSession) at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:700
 [14] write_compound at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:694 [inlined]
 [15] #_write#23 at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:690 [inlined]
 [16] _write at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:690 [inlined]
 [17] #write#17(::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::typeof(write), ::JLD.JldFile, ::String, ::OrderedDict{String,Array{Float64,1}}, ::JLD.JldWriteSession) at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:514
 [18] write at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:514 [inlined]
 [19] #35 at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:1223 [inlined]
 [20] #jldopen#14(::Base.Iterators.Pairs{Symbol,Bool,Tuple{Symbol,Symbol},NamedTuple{(:compatible, :compress),Tuple{Bool,Bool}}}, ::typeof(jldopen), ::getfield(JLD, Symbol("##35#36")){String,OrderedDict{String,Array{Float64,1}},Tuple{}},
::String, ::Vararg{String,N} where N) at C:\Users\Philip\.julia\packages\JLD\1BoSz\src\JLD.jl:246
 [21] testjld3() at .\none:0
 [22] top-level scope at REPL[48]:1

1 Ответ

0 голосов
/ 11 ноября 2019

Вместо этого используйте JLD2:

using JLD2, DataStructures, FileIO
function testjld2()
    res = OrderedDict("A" => 1, "B" => 2)
    myfilename = "c:/temp/test.jld2"
    save(myfilename, "res", res)
    res2 = load(myfilename)["res"]
    #Check if round-tripping works
    res == res2
end

Тестирование:

julia> testjld2()
true

Лично я всегда могу использовать BJSON:

using DataStructures, BSON, OrderedCollections
function testbson()
    res = OrderedDict("A" => 1, "B" => 2)
    myfilename = "c:/temp/test.bjson"
    BSON.bson(myfilename, Dict("res" => res))
    res2 = BSON.load(myfilename)["res"]
    #Check if round-tripping works
    res == res2
end
julia> testbson()
true
...