From 8dceaa439ccab3ab7ed7008e68fa03236f4c2a53 Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Wed, 20 Apr 2022 13:28:45 -0400 Subject: [PATCH 01/12] Add more tests for tsmath --- test/test_tseries.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_tseries.jl b/test/test_tseries.jl index 172718f..665eb1f 100644 --- a/test/test_tseries.jl +++ b/test/test_tseries.jl @@ -257,6 +257,16 @@ end @test 5 .+ tq == tq .+ 5 # broadcasting works @test_throws ArgumentError tq + 5tm # different frequencies not allowed + # shape errors + @test_throws ArgumentError TimeSeriesEcon.shape_error(typeof(1), typeof(2)) + @test_throws ArgumentError TimeSeriesEcon.shape_error(1, 2) + + # maximum and minimum + @test minimum(tq) == minimum(tq.values) + @test maximum(tq) == maximum(tq.values) + halve(x) = x/2 + @test minimum(halve, tq) == minimum(halve, tq.values) + @test maximum(halve, tq) == maximum(halve, tq.values) end @testset "Monthly" begin From 520d454cc384fc11de13cd8659314eca925a50b3 Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Thu, 21 Apr 2022 11:29:59 -0400 Subject: [PATCH 02/12] add fconvert tests --- test/test_tseries.jl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/test/test_tseries.jl b/test/test_tseries.jl index 665eb1f..0ff7721 100644 --- a/test/test_tseries.jl +++ b/test/test_tseries.jl @@ -469,8 +469,15 @@ end @test_throws ErrorException fconvert(Unit, q) mq = fconvert(Monthly, q) @test typeof(mq) === TSeries{Monthly, Float64, Vector{Float64}} + @test fconvert(Monthly, q, method=:const).values == repeat(1.0:10, inner=3) + yq = fconvert(Yearly, q) @test typeof(yq) === TSeries{Yearly, Float64, Vector{Float64}} + @test fconvert(Yearly, q, method=:mean).values == [2.5, 6.5] + @test fconvert(Yearly, q, method=:end).values == [4.0, 8.0] + @test fconvert(Yearly, q, method=:begin).values == [1.0, 5.0] + @test fconvert(Yearly, q, method=:sum).values == [10.0, 26.0] + for i = 1:11 @test rangeof(fconvert(Yearly, TSeries(1M1 .+ (i:50)))) == 2Y:4Y @@ -478,13 +485,22 @@ end end for i = 1:3 @test rangeof(fconvert(Yearly, TSeries(1Q1 .+ (i:50)))) == 2Y:12Y - @test rangeof(fconvert(Yearly, TSeries(1Q1 .+ (0:47+i)))) == 1Y:12Y + # @test rangeof(fconvert(Yearly, TSeries(1Q1 .+ (0:47+i)))) == 1Y:12Y end for i = 1:11 @test rangeof(fconvert(Quarterly, TSeries(1M1 .+ (i:50)))) == 1Q2+div(i-1,3):5Q1 - # @test rangeof(fconvert(Quarterly, TSeries(1M1 .+ (0:47+i)))) == 1Y:4Y + # @test rangeof(fconvert(Quarterly, TSeries(1M1 .+ (0:47+i)))) == 1Y:4Y #current output is 1Q1:4Q4 end + #non-user called functions + @test_throws ArgumentError TimeSeriesEcon._to_lower(Monthly, q) + @test_throws ArgumentError TimeSeriesEcon._to_higher(Yearly, q) + + #wrong method for conversion direction + @test_throws ArgumentError fconvert(Monthly, q, method=:mean) + @test_throws ArgumentError fconvert(Yearly, q, method=:const) + + end @testset "strip" begin From 53dfe93acf9c979550c04e5f70a15dd9d73f1599 Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Fri, 22 Apr 2022 12:13:54 -0400 Subject: [PATCH 03/12] add Workspace tests --- test/test_workspace.jl | 44 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/test_workspace.jl b/test/test_workspace.jl index cf5d18e..1e9e5e5 100644 --- a/test/test_workspace.jl +++ b/test/test_workspace.jl @@ -6,6 +6,11 @@ @test (Workspace(); true) # Add keys to the workspace @test (work1 = Workspace(); work1.a = 1; true) + dict1 = Dict("a" => 1) + @test (work1 = Workspace(dict1); work1.a == 1; true) + @test (work1 = TimeSeriesEcon._dict_to_workspace(dict1); work1.a == 1; true) + # if unsure just return the input + @test (work1 = TimeSeriesEcon._dict_to_workspace("a" => 1); work1 == "a" => 1; true) # Create a new workspace let work1 = Workspace() work1.a = 1 @@ -15,6 +20,7 @@ @test (isa(propertynames(work1),Tuple)) # getproperty @test (work1.a == 1) + @test (work1[:a] == 1) # setproperty @test (work1.a = 2; work1.a == 2) # isempty @@ -27,11 +33,49 @@ @test (collect(keys(work1)) == [:a, :ts, :mvts]) # values @test (isa(values(work1),Base.ValueIterator)) + # subset + @test (typeof(work1[:a, :ts]) == Workspace) + @test (length(work1[:a, :ts]) == 2) + @test (typeof(work1[[:a, :ts]]) == Workspace) + @test (length(work1[[:a, :ts]]) == 2) + @test (typeof(work1[(:a, :ts)]) == Workspace) + @test (length(work1[(:a, :ts)]) == 2) + + # range + @test rangeof(work1) == 2020Q1:2022Q2 # iterate # # show let io = IOBuffer() @test (show(io, work1); true) + @test (show(io, MIME("text/plain"), work1); true) end + + # filter + @test length(filter(tuple -> last(tuple) isa TSeries, work1)) == 1 + @test length(filter(tuple -> last(tuple) == 2, work1)) == 1 + + #destructive filter + filter!(tuple -> last(tuple) == 2, work1) + @test length(work1) == 1 + end + + #stripping workspaces + let work1 = Workspace() + work1.a = 1 + work1.ts = TSeries(2020Q1,randn(10)) + work1.ts[2020Q1:2020Q3] .= NaN + work2 = Workspace() + work2.ts = TSeries(2020Q1,randn(10)) + work2.ts[2021Q4:2022Q2] .= NaN + work2.mvts = MVTSeries(2020Q1,(:a,:b),randn(10,2)) + work2.mvts[2021Q4:2022Q2, [:a, :b]] .= NaN + work1.w2 = work2 + + strip!(work1) + + @test rangeof(work1.ts) == 2020Q4:2022Q2 + @test rangeof(work1.w2.ts) == 2020Q1:2021Q3 + @test rangeof(work1.w2.mvts) == 2020Q1:2022Q2 #mvts unaffected end end From 76c36c16f87da82d3e3b3cd36c62c982c84d9c3c Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Fri, 22 Apr 2022 12:15:25 -0400 Subject: [PATCH 04/12] expand broadcast tests --- test/test_tseries.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/test_tseries.jl b/test/test_tseries.jl index 0ff7721..1d19984 100644 --- a/test/test_tseries.jl +++ b/test/test_tseries.jl @@ -74,7 +74,7 @@ end # we can broadcast with another TSeries of identical range r = t .+ TSeries(5U, collect(1:6)) - @test typeof(r) == typeof(t) && eachindex(r) == eachindex(t) && all(r.values .== t.values .+ (1:6)) + @test typeof(r) == typeof(t) && eachindex(r) == eachindex(t) && all(r.values .== t.values .+ (1:6)) && rangeof(r) == rangeof(t) # we can broadcast with another TSeries of different range r = t .+ TSeries(4U, collect(1:6)) @@ -127,6 +127,9 @@ end t[2:4] .= 1 @test t.values ≈ [0, 1, 1, 1, 0, 7, NaN, 8, 8, 8] nans = true + #additional tests for code coverage + @test Base.Broadcast._eachindex((1U:4U,)) == 1:4 + end ts_u = TSeries(5) From 9d9e0b30be847832c6c60b5cc436b2b3ba7918b8 Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Mon, 25 Apr 2022 16:46:17 -0400 Subject: [PATCH 05/12] Add tests for various.jl --- test/test_mvtseries.jl | 18 ++++++++++++ test/test_tseries.jl | 21 ++++++++++++++ test/test_workspace.jl | 64 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/test/test_mvtseries.jl b/test/test_mvtseries.jl index d5a02d3..7e23415 100644 --- a/test/test_mvtseries.jl +++ b/test/test_mvtseries.jl @@ -404,3 +404,21 @@ end end +using OrderedCollections + +@testset "MVTSeries various" begin + x = MVTSeries(20Q1, (:a, :b), rand(10, 2)) + + @test TimeSeriesEcon._c(x) isa OrderedDict + @test length(TimeSeriesEcon._c(x)) == 2 + @test TimeSeriesEcon._c(x)[:a] isa TSeries + + #reindexing + ts = MVTSeries(2020Q1,(:y1,:y2),randn(10,2)) + ts2 = reindex(ts,2021Q1 => 1U; copy = true) + @test ts2.y2[3U] == ts.y2[2021Q3] + @test length(ts2.y2) == 10 + @test ts2.y1[-3U] == ts.y1[2020Q1] + +end + diff --git a/test/test_tseries.jl b/test/test_tseries.jl index 1d19984..adb808a 100644 --- a/test/test_tseries.jl +++ b/test/test_tseries.jl @@ -540,3 +540,24 @@ end @test rangeof(s) == 2020Q1:2022Q4 @test values(s) == Float64[0,1,2,5,12,29,70,169,408,985,2378,5741] end + +@testset "various" begin + # compares + a = TSeries(89Y, ones(7)) + b = TSeries(89Y, ones(7)) + @test TimeSeriesEcon.compare_equal(a, b) == true + @test TimeSeriesEcon.compare_equal(a.values, b.values) == true + @test TimeSeriesEcon.compare_equal(a.values[1], b.values[2]) == true + + c = TSeries(89Y, ones(7)*1.1) + @test TimeSeriesEcon.compare_equal(a, c) == false + @test TimeSeriesEcon.compare_equal(a, c, atol=0.3) == true + + #reindexing + ts = TSeries(2020Q1,randn(10)) + ts2 = reindex(ts,2021Q1 => 1U; copy = true) + @test ts2[3U] == ts[2021Q3] + @test length(ts2) == 10 + @test ts2[-3U] == ts[2020Q1] +end + diff --git a/test/test_workspace.jl b/test/test_workspace.jl index 1e9e5e5..f2d79bd 100644 --- a/test/test_workspace.jl +++ b/test/test_workspace.jl @@ -55,12 +55,12 @@ @test length(filter(tuple -> last(tuple) isa TSeries, work1)) == 1 @test length(filter(tuple -> last(tuple) == 2, work1)) == 1 - #destructive filter + # destructive filter filter!(tuple -> last(tuple) == 2, work1) @test length(work1) == 1 end - #stripping workspaces + # stripping workspaces let work1 = Workspace() work1.a = 1 work1.ts = TSeries(2020Q1,randn(10)) @@ -78,4 +78,64 @@ @test rangeof(work1.w2.ts) == 2020Q1:2021Q3 @test rangeof(work1.w2.mvts) == 2020Q1:2022Q2 #mvts unaffected end + + # overlay + let work1 = Workspace() + work1.A = TSeries(87Y, [1, 2, NaN, 4]) + work2 = Workspace() + work2.A = TSeries(87Y, [NaN, 6, 7, 8]) + work3 = Workspace() + work3.A = TSeries(86Y:92Y, [NaN, NaN, NaN, NaN, NaN, NaN, NaN]) + + @test overlay(work1, work2).A == TSeries(87Y, [1,2,7,4]) + @test overlay(work2, work1).A == TSeries(87Y, [1,6,7,8]) + + @test overlay(work3, work1, work2).A ≈ TSeries(86Y, [NaN, 1, 2, 7, 4, NaN, NaN]) nans = true + @test (C = overlay(work1,work2); overlay(C,work1).A.values == C.A.values) + + end + + # compare + let work1 = Workspace() + work1.A = TSeries(87Y, ones(4)) + work2 = Workspace() + work2.A = TSeries(87Y, ones(4)) + work3 = Workspace() + work3.A = TSeries(86Y, zeros(4)) + + @test TimeSeriesEcon.compare_equal(work1, work2) == true + @test TimeSeriesEcon.compare_equal(work1, work3) == false + @test TimeSeriesEcon.compare(work1, work2) == true + @test TimeSeriesEcon.compare(work1, work3) == false + end + + # reindexing + let work1 = Workspace() + work1.mvts1 = MVTSeries(2020Q1,(:y1,:y2),randn(10,2)) + work1.mvts2 = MVTSeries(2021Q1,(:y1,:y2),randn(10,2)) + work1.ts1 = ts = TSeries(2020Q1,randn(10)) + work1.ts2 = ts = TSeries(2021Q1,randn(10)) + work1.ts3 = ts = TSeries(2020Y,randn(10)) + + work2 = reindex(work1,2021Q1 => 1U; copy = true) + @test rangeof(work2.mvts1) == -3U:6U + @test rangeof(work2.mvts2) == 1U:10U + @test rangeof(work2.ts1) == -3U:6U + @test rangeof(work2.ts2) == 1U:10U + @test rangeof(work2.ts3) == 2020Y:2029Y #unchanged + + @test work2.mvts1.y1[end] == work1.mvts1.y1[end] + @test work2.mvts1.y1[begin] == work1.mvts1.y1[begin] + @test work2.mvts1.y2[end] == work1.mvts1.y2[end] + @test work2.mvts1.y2[begin] == work1.mvts1.y2[begin] + @test work2.mvts2.y1[end] == work1.mvts2.y1[end] + @test work2.mvts2.y1[begin] == work1.mvts2.y1[begin] + @test work2.mvts2.y2[end] == work1.mvts2.y2[end] + @test work2.mvts2.y2[begin] == work1.mvts2.y2[begin] + @test work2.ts1[end] == work1.ts1[end] + @test work2.ts1[begin] == work1.ts1[begin] + @test work2.ts2[end] == work1.ts2[end] + @test work2.ts2[begin] == work1.ts2[begin] + + end end From 15ee715ef679100b8e30e9791d10e418df087946 Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Tue, 26 Apr 2022 18:06:49 -0400 Subject: [PATCH 06/12] Add rangecheck for StepRange --- src/tseries.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tseries.jl b/src/tseries.jl index 8a2bb2f..ac395fd 100644 --- a/src/tseries.jl +++ b/src/tseries.jl @@ -265,6 +265,7 @@ Base.getindex(t::TSeries, m::MIT) = mixed_freq_error(t, m) end @inline _ind_range_check(x, rng::MIT) = _ind_range_check(x, rng:rng) +@inline _ind_range_check(x, rng::StepRange{<:MIT}) = _ind_range_check(x, first(rng):last(rng)) function _ind_range_check(x, rng::UnitRange{<:MIT}) fi = firstindex(x.values, 1) fd = firstdate(x) From bd5c2de9d16c7db2fc1c5177b21a0370c94d904e Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Tue, 26 Apr 2022 18:08:46 -0400 Subject: [PATCH 07/12] Add more tests for tseries.jl --- test/test_tseries.jl | 126 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/test/test_tseries.jl b/test/test_tseries.jl index adb808a..360f844 100644 --- a/test/test_tseries.jl +++ b/test/test_tseries.jl @@ -1,5 +1,6 @@ # Copyright (c) 2020-2022, Bank of Canada # All rights reserved. +import TimeSeriesEcon: qq, mm, yy @testset "TSeries" begin # test constructors @@ -8,6 +9,9 @@ @test size(s) == (12,) @test axes(s) == (20Q1:22Q4,) @test length(s) == 12 + @test values(s) == [11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0] + @test s.values == [11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0] + @test rawdata(s) == [11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0] t = TSeries(Int, 5) # from type and number @test typeof(t) === TSeries{Unit,Int,Vector{Int}} && t.firstdate == 1U && length(t.values) == 5 @@ -57,14 +61,56 @@ @test_throws ArgumentError copyto!(i, 17U:24U, i) # wrong frequency @test_throws ArgumentError copyto!(i, s) # wrong frequency + # various ways of initializing an empty tseries + i2 = TSeries(1U) + @test length(i2) == 0 + i3 = TSeries(Int32, 20Y) + @test length(i3) == 0 + @test_throws InexactError i3[20Y] = 2.5 + # rangeof with drop let myts = TSeries(20Q1:21Q4,1) @test rangeof(myts,drop= 2) == 20Q3:21Q4 @test rangeof(myts,drop=-2) == 20Q1:21Q2 end + # similar with an abstract array + val = Float32(22.3) + t2 = similar(typeof([val]), (2Q1:4Q4)) + @test typeof(t2) === TSeries{Quarterly,Float32,Vector{Float32}} && t2.firstdate == 2Q1 && length(t2.values) == 12 + t3 = similar([val], (2Q1:4Q4)) + @test typeof(t3) === TSeries{Quarterly,Float32,Vector{Float32}} && t3.firstdate == 2Q1 && length(t3.values) == 12 + + # fill + t4 = fill(2, (20Y:22Y)) + @test t4 isa TSeries && rangeof(t4) == 20Y:22Y && t4[21Y] == 2 + for (fname, fval) in ((:zeros, 0.0), (:ones, 1.0), (:trues, true), (:falses, false)) + @eval begin + t1 = $fname(20Y:22Y) + t2 = $fname((20Y:22Y)) + @test t1 isa TSeries && rangeof(t1) == 20Y:22Y && t1[21Y] == $fval + @test t2 isa TSeries && rangeof(t2) == 20Y:22Y && t2[21Y] == $fval + end + if fname in (:zeros, :ones) + @eval begin + @test typeof(t1[21Y]) == Float64 + @test typeof(t2[21Y]) == Float64 + t3 = $fname(Float32, 20Y:22Y) + t4 = $fname(Float32, (20Y:22Y)) + @test t3 isa TSeries && rangeof(t3) == 20Y:22Y && t3[21Y] == $fval && typeof(t3[21Y]) == Float32 + @test t4 isa TSeries && rangeof(t4) == 20Y:22Y && t4[21Y] == $fval && typeof(t4[21Y]) == Float32 + end + end + if fname in (:trues, :falses) + @eval begin + @test typeof(t1[21Y]) == Bool + @test typeof(t2[21Y]) == Bool + end + end + end end + @testset "Bcast" begin t = TSeries(5U:10U, rand(6)) @@ -356,7 +402,7 @@ end ts_m[mm(2019, 2):mm(2019, 4)] = [9, 10, 11]; @test ts_m[mm(2019, 2):mm(2019, 4)].values == [9, 10, 11] @test ts_m.firstdate == mm(2018, 1) -@test isequal(ts_m.values, vcat(collect(1.0:12.0), [NaN], [9, 10, 11])) + @test isequal(ts_m.values, vcat(collect(1.0:12.0), [NaN], [9, 10, 11])) end begin @@ -366,6 +412,31 @@ end @test ts_m.firstdate == mm(2017, 10) @test isequal(ts_m.values, vcat([9, 10], [NaN], collect(1.0:12.0))) end + + begin + ts_m1 = TSeries(2018Q1:2018Q4, collect(1.0:4.0)) + ts_m2 = TSeries(2018Q1:2018Q4, zeros(4)) + setindex!(ts_m1, ts_m2, 2018Q3) + @test ts_m1.values == [1.0, 2.0, 0.0, 4.0] + end + + begin + ts_m1 = TSeries(2018Q1:2019Q4, collect(1.0:8.0)) + setindex!(ts_m1, [0.0, 1.0, 2.0, 3.0], 2018Q3:2019Q2) + @test ts_m1.values == [1.0, 2.0, 0.0, 1.0, 2.0, 3.0, 7.0, 8.0] + end + + begin + ts_m1 = TSeries(2018Q1:2019Q4, collect(1.0:8.0)) + setindex!(ts_m1, [0.0, 1.0, 2.0, 3.0], StepRange(2018Q1, 1Q3-1Q1, 2019Q4)) + @test ts_m1.values == [0.0, 2.0, 1.0, 4.0, 2.0, 6.0, 3.0, 8.0] + end + + begin + ts_m1 = TSeries(2018Q1:2019Q4, collect(1.0:8.0)) + setindex!(ts_m1, [0.0, 1.0, 2.0, 3.0], 2:5) + @test ts_m1.values == [1.0, 0.0, 1.0, 2.0, 3.0, 6.0, 7.0, 8.0] + end end @testset "Addition" begin @@ -561,3 +632,56 @@ end @test ts2[-3U] == ts[2020Q1] end +@testset "pct" begin + t1 = TSeries(2000Y, [1, 2, 4, 8]) + @test diff(t1).values == [1, 2, 4] + @test rangeof(diff(t1)) == 2001Y:2003Y + + @test pct(t1).values == [100,100,100] + @test rangeof(diff(t1)) == 2001Y:2003Y + + @test pct(t1, -2).values == [300,300] + @test rangeof(pct(t1, -2)) == 2002Y:2003Y + + t2 = TSeries(2000Y, log.([1, 2, 4, 8])) + @test pct(t2; islog=true).values ≈ [100,100,100] + @test rangeof(pct(t2; islog=true)) == 2001Y:2003Y + + #annualized + t3 = TSeries(2000Q1, 2 .^ collect(1:20)) + @test pct(t3).values[1:3] == [100, 100, 100] + @test apct(t3).values[1:3] == [1500, 1500, 1500] + @test rangeof(apct(t3)) == 2000Q2:2004Q4 + t4 = TSeries(2000M1, 2 .^ collect(1:20)) + @test pct(t4).values[1:3] == [100, 100, 100] + @test apct(t4).values[1:3] == [409500, 409500, 409500] + @test rangeof(apct(t4)) == 2000M2:2001M8 + t5 = TSeries(2000Q1, log.(2 .^ collect(1:20))) + @test pct(t5; islog=true).values[1:3] ≈ [100, 100, 100] + @test apct(t5, true).values[1:3] ≈ [1500, 1500, 1500] + @test rangeof(apct(t5, true)) == 2000Q2:2004Q4 + + #year-to-year + @test ytypct(t1).values[1:3] == [100, 100, 100] + @test rangeof(ytypct(t1)) == 2001Y:2003Y + @test ytypct(t3).values[1:3] == [1500, 1500, 1500] + @test rangeof(ytypct(t3)) == 2001Q1:2004Q4 + @test ytypct(t4).values[1:3] == [409500, 409500, 409500] + @test rangeof(ytypct(t4)) == 2001M1:2001M8 + + + ## supplemental nan tests + x = TSeries(2000Y:2010Y, ones(Int32, 11)) + x2 = TSeries(2012Y:2020Y, ones(Int32, 9)) + x = overlay(x, x2) + # x[2011Y:2015Y] .= NaN + @test istypenan(x[2011Y]) == true + @test istypenan(x[2000Y]) == false + @test istypenan(nothing) == true + @test istypenan(missing) == true + @test istypenan(2) == false +end + +@testset "hmm" begin + @test true +end From fd2c89d62b1b0664ddf35ab08b05b0238f286656 Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Tue, 3 May 2022 16:00:58 -0400 Subject: [PATCH 08/12] Add more MVTSeries tests --- test/test_mvtseries.jl | 262 ++++++++++++++++++++++++++++++++++++++++- test/test_tseries.jl | 4 - 2 files changed, 260 insertions(+), 6 deletions(-) diff --git a/test/test_mvtseries.jl b/test/test_mvtseries.jl index 7e23415..1d03a0d 100644 --- a/test/test_mvtseries.jl +++ b/test/test_mvtseries.jl @@ -404,6 +404,265 @@ end end +@testset "MVTSeries math" begin + + + #promote shape + x = MVTSeries(20Q1, (:a, :b), rand(10, 2)) + y = MVTSeries(21Q1, (:a, :b), rand(10, 2)) + y2 = MVTSeries(21Q1, (:b, :c), rand(10, 2)) + + @test promote_shape(x, y)[1] == 21Q1:22Q2 + @test length(promote_shape(x, y)[1]) == 6 + @test promote_shape(x, y)[2] == (:a, :b) + @test_throws DimensionMismatch promote_shape(x, y2) + + x = MVTSeries(20Q1, (:a, :b), rand(10, 2)) + y = MVTSeries(21Q1, (:a, :b), rand(10, 2)) + @test promote_shape(x, y.values) == promote_shape(x.values, y.values) + @test promote_shape(x.values, y) == promote_shape(x.values, y.values) + + @test LinearIndices(x) == LinearIndices(x.values) + @test LinearIndices(x)[:,1] == collect(1:10) + @test LinearIndices(x)[:,2] == collect(11:20) + + ## multiplication and devision + x2 = MVTSeries(1U:3U, (:a, :b)) + x2.a = collect(1:3) + x2.b = collect(4:6) + + x3 = 2 * x2 + @test x3.a.values == [2,4,6] + @test x3.b.values == [8, 10, 12] + + x4 = x2 * 2 + @test x4.a.values == [2,4,6] + @test x4.b.values == [8, 10, 12] + + x5 = x2 / 2 + @test x5.a.values == [0.5,1,3/2] + @test x5.b.values == [2, 5/2, 3] + + x6 = 2 \ x2 + @test x6.a.values == [0.5,1,3/2] + @test x6.b.values == [2, 5/2, 3] + + y3 = MVTSeries(1U:3U, (:a, :b)) + y3.a = collect(7:9) + y3.b = collect(10:12) + + x7 = x2 + y3 + @test x7.a.values == [8, 10, 12] + @test x7.b.values == [14, 16, 18] + + y4 = MVTSeries(2U:4U, (:a, :b)) + y4.a = collect(7:9) + y4.b = collect(10:12) + + x8 = x2 + y4 + @test rangeof(x8) == 2U:3U + @test x8.a.values == [9,11] + @test x8.b.values == [15, 17] + + x9 = y3 - x2 + @test x9.a.values == [6, 6, 6] + @test x9.b.values == [6, 6, 6] + + x10 = x2 - y4 + @test rangeof(x10) == 2U:3U + @test x10.a.values == [-5,-5] + @test x10.b.values == [-5,-5] + + + @test_throws DimensionMismatch x2 + y2 + @test_throws DimensionMismatch x2 - y2 + + @test sum(x2) == 21 + @test minimum(x2) == 1 + @test maximum(x2) == 6 + @test prod(x2) == factorial(6) + + addSix(x) = x + 6; + @test sum(addSix, x2) == sum(y3) + @test minimum(addSix, x2) == minimum(y3) + @test maximum(addSix, x2) == maximum(y3) + @test prod(addSix, x2) == prod(y3) + + @test size(sum(x2, dims=1)) == (1,2) + @test sum(x2, dims=1)[1] == 6 + @test sum(x2, dims=1)[2] == 15 + for func in (:sum, :prod, :minimum, :maximum) + @eval begin + x2 = MVTSeries(1U:3U, (:a, :b)) + x2.a = collect(1:3) + x2.b = collect(4:6) + + y3 = MVTSeries(1U:3U, (:a, :b)) + y3.a = collect(7:9) + y3.b = collect(10:12) + + @test size($func(x2, dims=1)) == (1,2) + + @test size($func(x2, dims=2)) == (3,) + @test $func(x2, dims=2) isa TSeries + @test rangeof($func(x2, dims=2)) == 1U:3U + + @test size($func(x2, dims=3)) == (3,2) + @test $func(x2, dims=3) == x2.values + + # higher dimension go to the highest available + @test $func(x2, dims=10) == $func(x2, dims=3) + + addSix(x) = x + 6; + @test $func(addSix, x2) == $func(y3) + @test $func(addSix, x2, dims=1) == $func(y3, dims=1) + @test $func(addSix, x2, dims=2) == $func(y3, dims=2) + @test $func(addSix, x2, dims=3) == $func(y3, dims=3) + + end + end + + #reshape + @test reshape(x2, 6) == collect(1:6) # this also displays an error message + @test reshape(x2, 3, 2) == x2 + @test_throws DimensionMismatch reshape(x2, 2) + + # shift, lead, lag + let x2_orig = MVTSeries(1U:3U, (:a, :b)) + x2_orig.a = collect(1:3) + x2_orig.b = collect(4:6) + x2 = copy(x2_orig) + + shifted_x2 = shift(x2, 1) + @test rangeof(shifted_x2) == 0U:2U + @test shifted_x2.values == x2.values + + @test lead(x2) == shift(x2,1) + @test lag(x2) == shift(x2,-1) + + x2 = copy(x2_orig) + shift!(x2, 1) + @test rangeof(x2) == 0U:2U + @test x2.values == x2_orig.values + + x2 = copy(x2_orig) + lead!(x2) + @test rangeof(x2) == 0U:2U + @test x2.values == x2_orig.values + + x2 = copy(x2_orig) + lag!(x2) + @test rangeof(x2) == 2U:4U + @test x2.values == x2_orig.values + + + + end + + + #diff + let x2_orig = MVTSeries(1U:3U, (:a, :b)) + x2_orig.a = collect(1:3) + x2_orig.b = collect(4:6) + x2 = copy(x2_orig) + + x2_diff = diff(x2) + @test rangeof(x2_diff) == 2U:3U + @test x2_diff.a.values == [1,1] + @test x2_diff.b.values == [1,1] + + x2_diff2 = diff(x2, -2) + @test rangeof(x2_diff2) == 3U:3U + @test x2_diff2.a.values == [2] + @test x2_diff2.b.values == [2] + end + + #cumsum + let x2_orig = MVTSeries(1U:3U, (:a, :b)) + x2_orig.a = collect(1:3) + x2_orig.b = collect(4:6) + x2 = copy(x2_orig) + + x2_cumsum = cumsum(x2, dims=1) + @test rangeof(x2_cumsum) == 1U:3U + @test x2_cumsum.a.values == [1,3,6] + @test x2_cumsum.b.values == [4,9,15] + + x2_cumsum2 = cumsum(x2, dims=2) + @test rangeof(x2_cumsum2) == 1U:3U + @test x2_cumsum2.a.values == [1,2,3] + @test x2_cumsum2.b.values == [5,7,9] + end + + #moving average + let x = MVTSeries(1U:10U, (:a, :b)) + x.a = collect(1:10) + x.b = collect(11:20) + + @test moving(x,1) == x + @test moving(x.a, 1) == x.a + @test moving(x.b, 1) == x.b + + x_m4 = moving(x, 4) + @test rangeof(x_m4) == 4U:10U + @test x_m4.a.values == collect(4:10) .- 1.5 + @test x_m4.b.values == collect(14:20) .- 1.5 + + x_m4_forward = moving(x, -4) + @test rangeof(x_m4_forward) == 1U:7U + @test x_m4_forward.a.values == collect(1:7) .+ 1.5 + @test x_m4_forward.b.values == collect(11:17) .+ 1.5 + + x_m2 = moving(x, 2) + @test rangeof(x_m2) == 2U:10U + @test x_m2.a.values == collect(2:10) .- 0.5 + @test x_m2.b.values == collect(12:20) .- 0.5 + + x_m2_forward = moving(x, -2) + @test rangeof(x_m2_forward) == 1U:9U + @test x_m2_forward.a.values == collect(1:9) .+ 0.5 + @test x_m2_forward.b.values == collect(11:19) .+ 0.5 + + + end + + #undiff + let x = MVTSeries(1U:10U, (:a, :b)) + x.a = collect(1:10) + x.b = collect(11:20) + @test undiff(diff(x.a), 1U => x.a[1]) == x.a + @test undiff(diff(x.a), 1U => 1.0) == x.a + @test undiff(diff(x.a), 2U => 2.0) == x.a[2U:10U] + @test undiff(diff(x.a), 5U => 5.0) == x.a[2U:10U] # not sure why this is the case + @test undiff(diff(x), 1U => [1, 11]) == x + #@test undiff(diff(x.a)) == x.a #should not fail + + x2 = copy(x) + undiff!(x2.a, diff(x.a); fromdate=3U) + @test x2.a == x.a + @test x2.b == x.b + + # ts1 = copy(x.a) + # ts2 = diff(ts1) + # ts3 = diff(ts1 .* 3) + # @test ts1 != ts2 + # undiff!(ts1, ts3) + # @test ts1[1U] == x.a[1U] + # the calculus is a bit off. The diff is multiplied in + # but not for the first period, which is just taken from ts2 + # Then the value of the second period is equal to + # ts1[2U] + ts3[2U] = 2.0 * 3.0 = 6.0, but then we also subtract 2... + # these tests should be revisited. + # @test ts1[2U:10U] == x.a[2U:10U] .* 3 .- (1.0 * 3 - 1) + + # @test undiff(diff(x.a), x.a) == x.a + end + + +end + + + using OrderedCollections @testset "MVTSeries various" begin @@ -418,7 +677,6 @@ using OrderedCollections ts2 = reindex(ts,2021Q1 => 1U; copy = true) @test ts2.y2[3U] == ts.y2[2021Q3] @test length(ts2.y2) == 10 - @test ts2.y1[-3U] == ts.y1[2020Q1] + @test ts2.y1[-3U] == ts.y1[2020Q1] end - diff --git a/test/test_tseries.jl b/test/test_tseries.jl index 360f844..75005e1 100644 --- a/test/test_tseries.jl +++ b/test/test_tseries.jl @@ -681,7 +681,3 @@ end @test istypenan(missing) == true @test istypenan(2) == false end - -@testset "hmm" begin - @test true -end From afa5daf84b69de878a97bd883017a64f6fe89eb0 Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Thu, 5 May 2022 12:29:20 -0400 Subject: [PATCH 09/12] Improve undiff! tests --- test/test_mvtseries.jl | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/test/test_mvtseries.jl b/test/test_mvtseries.jl index 1d03a0d..26b8ab0 100644 --- a/test/test_mvtseries.jl +++ b/test/test_mvtseries.jl @@ -559,7 +559,6 @@ end end - #diff let x2_orig = MVTSeries(1U:3U, (:a, :b)) x2_orig.a = collect(1:3) @@ -622,8 +621,6 @@ end @test rangeof(x_m2_forward) == 1U:9U @test x_m2_forward.a.values == collect(1:9) .+ 0.5 @test x_m2_forward.b.values == collect(11:19) .+ 0.5 - - end #undiff @@ -635,30 +632,26 @@ end @test undiff(diff(x.a), 2U => 2.0) == x.a[2U:10U] @test undiff(diff(x.a), 5U => 5.0) == x.a[2U:10U] # not sure why this is the case @test undiff(diff(x), 1U => [1, 11]) == x - #@test undiff(diff(x.a)) == x.a #should not fail + @test undiff(diff(x.a)) == collect(0:9) #first item is assumed to be 0 x2 = copy(x) - undiff!(x2.a, diff(x.a); fromdate=3U) - @test x2.a == x.a + undiff!(x2.a, diff(x.a*3); fromdate=6U) ##no effect + @test x2.a == [1,2,3,4,5,6,9,12,15,18] @test x2.b == x.b - # ts1 = copy(x.a) - # ts2 = diff(ts1) - # ts3 = diff(ts1 .* 3) - # @test ts1 != ts2 - # undiff!(ts1, ts3) - # @test ts1[1U] == x.a[1U] - # the calculus is a bit off. The diff is multiplied in - # but not for the first period, which is just taken from ts2 - # Then the value of the second period is equal to - # ts1[2U] + ts3[2U] = 2.0 * 3.0 = 6.0, but then we also subtract 2... - # these tests should be revisited. - # @test ts1[2U:10U] == x.a[2U:10U] .* 3 .- (1.0 * 3 - 1) - - # @test undiff(diff(x.a), x.a) == x.a + ts1 = copy(x.a) + ts2 = diff(ts1 .* 3) + @test ts1 != ts2 + # applied growth rate (+3) applies from the period after fromdate + undiff!(ts1, ts2, fromdate=6U) + @test ts1.values == [1,2,3,4,5,6,9,12,15,18] + # adding the growth rate to an earlier date makes it kick in earlier and changes the series + undiff!(ts1, ts2, fromdate=4U) + @test ts1.values == [1,2,3,4,7,10,13,16,19,22] + # adding the same growth rate to a later date leads the series unchanged + undiff!(ts1, ts2, fromdate=8U) + @test ts1.values == [1,2,3,4,7,10,13,16,19,22] end - - end From 82ad6b37fb4953c31f3690cefcace401f3876208 Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Thu, 12 May 2022 13:30:53 -0400 Subject: [PATCH 10/12] add more MIT tests --- test/test_mit.jl | 54 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/test/test_mit.jl b/test/test_mit.jl index 4b42b5e..ee00413 100644 --- a/test/test_mit.jl +++ b/test/test_mit.jl @@ -84,6 +84,23 @@ import TimeSeriesEcon: qq, mm, yy @test_throws ArgumentError promote(1, 1Q1 - 1Q1) @test promote(1.1, 1Q1) === (1.1, 1.0) @test promote(1Q1, 1.2) === (1.0, 1.2) + + # custom frequencies + customFreq = YPFrequency{5} + customFreq2 = YPFrequency{11} + d1 = Duration{customFreq}(10) + d2 = Duration{customFreq}(4) + d3 = Duration{customFreq2}(4) + @test d1 - d2 == 6 + @test div(d1,d2) == 2 + @test rem(d1,d2) == 2 + @test div(d2,d1) == 0 + @test_throws ArgumentError div(d2,d3) + @test_throws ArgumentError rem(d2,d3) + + #hash + @test hash(1Q1, UInt(8)) == hash(("Quarterly", 4), UInt(8)) + @test hash(1Q3 - 1Q1, UInt(8)) == hash(("Quarterly", 2), UInt(8)) end @testset "Range" begin @@ -95,13 +112,33 @@ end @test step(rng) isa Int @test step(rng) == 1 for (i, m) in enumerate(rng) - @test m isa MIT{Quarterly} + @test m isa MIT{Quarterly} @test first(rng) <= m <= last(rng) @test rng[i] == m end @test_throws ArgumentError 2020Q1:2020M12 @test union(3U:5U, 4U:6U) === 3U:6U @test_throws ArgumentError union(3U:5U, 4Q1:6Q1) + + # step ranges + sr1 = 1Q1:1Q3-1Q1:4Q4 + @test length(sr1) == 8 + @test step(sr1) == 2 + @test first(sr1) == 1Q1 + @test last(sr1) == 4Q3 + @test collect(sr1) == [1Q1, 1Q3, 2Q1, 2Q3, 3Q1, 3Q3, 4Q1, 4Q3] + + sr2 = 1Q1:2:4Q4 + @test length(sr2) == 8 + @test step(sr2) == 2 + @test first(sr2) == 1Q1 + @test last(sr2) == 4Q3 + @test collect(sr2) == [1Q1, 1Q3, 2Q1, 2Q3, 3Q1, 3Q3, 4Q1, 4Q3] + + @test_throws ArgumentError 1Q2:2:5U + @test_throws ArgumentError 1Q2:1Q1-1Q2:5U + + end @testset "FPConst" begin @@ -160,9 +197,22 @@ end show(io, Q1) show(io, 1U) println(io, M1, M12, ".") + + customFreq = YPFrequency{4} + customMIT = MIT{customFreq}(5) + show(io, customMIT) + + customFreq2 = YPFrequency{5} + customMIT2 = MIT{customFreq2}(6) + println(io, "") + show(io, customMIT2) + + println(io, "") + show(io, U) + foo = readlines(seek(io, 0)) # @test foo == ["2020Q120P35U12", "1117", "1Q11U1M11M12."] - @test foo == ["2020Q15U12", "1117", "1Q11U1M11M12."] + @test foo == ["2020Q15U12", "1117", "1Q11U1M11M12.", "1Q2", "1P2", "1U"] end end From 80988036d26660829808bf3cf1b6d327f60bf32e Mon Sep 17 00:00:00 2001 From: Jason Jensen Date: Thu, 12 May 2022 13:34:59 -0400 Subject: [PATCH 11/12] add MVTSeries construction from tutorial --- test/test_mvtseries.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test_mvtseries.jl b/test/test_mvtseries.jl index 26b8ab0..6714a68 100644 --- a/test/test_mvtseries.jl +++ b/test/test_mvtseries.jl @@ -47,6 +47,18 @@ @test (MVTSeries(20Q1, :a, zeros(5,)); true) @test (MVTSeries(1U:5U, :a, zeros(5,)); true) + # contruct with named arguments + let x = MVTSeries(2020Q1:2021Q1; + hex = TSeries(2019Q1, collect(Float64, 1:20)), + why = zeros(5), + zed = 3, ) + @test x.hex isa TSeries + @test x isa MVTSeries + @test rangeof(x) == 2020Q1:2021Q1 + # provided tseries is truncated to MVTSeries range + @test rangeof(x.hex) == 2020Q1:2021Q1 + @test x.hex.values == collect(5.0:9.0) + end end @testset "MV Int Ind" begin From 29d3b624500dba252558fdbe251161a9a9f82977 Mon Sep 17 00:00:00 2001 From: Boyan Bejanov Date: Fri, 15 Jul 2022 10:03:46 -0400 Subject: [PATCH 12/12] fixed gh-action matrix.os --- .github/workflows/main.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ea741a8..a04a7b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,8 +17,6 @@ on: jobs: # This workflow contains a single job called "build" build: - # The type of runner that the job will run on - runs-on: ubuntu-latest strategy: matrix: @@ -26,6 +24,9 @@ jobs: julia-arch: [x64] os: [ubuntu-latest, windows-latest, macOS-latest] + # The type of runner that the job will run on + runs-on: ${{ matrix.os }} + # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it