From 7b27684c4da1046f574ca90b927be49d9b30de48 Mon Sep 17 00:00:00 2001 From: Dongdong Kong Date: Wed, 12 Jul 2023 13:46:21 +0800 Subject: [PATCH] minor update --- Project.toml | 2 +- src/Climate/Climate.jl | 2 +- src/Climate/climatology.jl | 2 +- src/Climate/threshold.jl | 148 ++++++++++----------------- src/Climate/threshold_3d.jl | 159 +++++++++++++++++++++++++++++ src/Climate/threshold_vec.jl | 128 ----------------------- src/tools.jl | 7 +- test/Statistics/test_vogel2020.jl | 2 +- test/Statistics/test_vogel2020.qmd | 2 +- test/runtests.jl | 1 + test/test-stat_threshold.jl | 4 +- test/test-tools.jl | 11 ++ 12 files changed, 238 insertions(+), 230 deletions(-) create mode 100644 src/Climate/threshold_3d.jl delete mode 100644 src/Climate/threshold_vec.jl create mode 100644 test/test-tools.jl diff --git a/Project.toml b/Project.toml index ecc07fc..1243a2d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Ipaper" uuid = "e58298cb-69f7-4186-aecd-5834b6793426" authors = ["Dongdong Kong "] -version = "0.1.8" +version = "0.1.9" [deps] CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" diff --git a/src/Climate/Climate.jl b/src/Climate/Climate.jl index 49be30a..49ef29b 100644 --- a/src/Climate/Climate.jl +++ b/src/Climate/Climate.jl @@ -1,9 +1,9 @@ include("base.jl") include("threshold.jl") +include("threshold_3d.jl") include("climatology.jl") include("anomaly.jl") include("warming_level.jl") -include("threshold_vec.jl") # export cal_mTRS_base, cal_mTRS_season, cal_mTRS_full export format_md diff --git a/src/Climate/climatology.jl b/src/Climate/climatology.jl index fa7b91a..7175ef0 100644 --- a/src/Climate/climatology.jl +++ b/src/Climate/climatology.jl @@ -4,7 +4,7 @@ $(TYPEDSIGNATURES) function cal_climatology_base!(Q::AbstractArray{T,3}, data::AbstractArray{T,3}, dates; use_mov=true, halfwin::Int=7, parallel::Bool=true, fun=nanmean, - type="md") where {T<:Real} + ignored...) where {T<:Real} mmdd = factor(format_md.(dates)).refs diff --git a/src/Climate/threshold.jl b/src/Climate/threshold.jl index 61bb45f..cf4293b 100644 --- a/src/Climate/threshold.jl +++ b/src/Climate/threshold.jl @@ -1,106 +1,65 @@ -export cal_mTRS_base, cal_mTRS_full +export Threshold +module Threshold -""" -Moving Threshold for Heatwaves Definition +export cal_mTRS_base +using Ipaper -$(TYPEDSIGNATURES) -# Arguments - -- `method_q`: method to calculate quantile, one of `base`, `mapslices`. - `base` is about 3 times faster and reduce used memory in 20 times. - -# References -1. Vogel, M. M., Zscheischler, J., Fischer, E. M., & Seneviratne, S. I. (2020). - Development of Future Heatwaves for Different Hazard Thresholds. Journal of - Geophysical Research: Atmospheres, 125(9). - https://doi.org/10.1029/2019JD032070 -""" -function cal_mTRS_base!(Q::AbstractArray{T}, data::AbstractArray{T}, dates; +function cal_mTRS_base!(Q::AbstractArray{T}, + arr::AbstractArray{T,N}, mmdd; + dims=N, probs::Vector=[0.90, 0.95, 0.99, 0.999, 0.9999], - use_mov=true, - halfwin::Int=7, - parallel::Bool=true, - method_q="base", na_rm=false, - ignore...) where {T<:Real} - - mmdd = factor(format_md.(dates)).refs - mds = mmdd |> unique_sort - doy_max = length(mds) + parallel=true, halfwin=7, use_mov=true, + kw...) where {T<:Real, N} + doy_max = maximum(mmdd) + @inbounds @par parallel for doy = 1:doy_max ind = filter_mds(mmdd, doy; doy_max, halfwin, use_mov) - - q = @view Q[:, :, doy, :] - x = @view data[:, :, ind] # 这一步耗费内存 - if method_q == "base" - NanQuantile_3d!(q, x; probs, dims=3, na_rm) - # NanQuantile_low!(q, x; probs, dims=3, na_rm) - elseif method_q == "mapslices" - q .= NanQuantile(x; probs, dims=3, na_rm) # mapslices is suppressed for 3d `NanQuantile` - end + # idx = ntuple(i -> i == dims ? ind : Colon(), N) + # ridx = ntuple(i -> i == dims ? doy : Colon(), N + 1) + # x = @view(arr[idx...]) + # q = @view(Q[ridx...]) + x = selectdim(arr, dims, ind) + q = selectdim(Q, dims, doy) + q .= NanQuantile(x; probs, dims) # mapslices end Q end -""" - $(TYPEDSIGNATURES) - -# Arguments -- `type`: The matching type of the moving `doys`, "md" (default) or "doy". - -# Return -- `TRS`: in the dimension of `[nlat, nlon, ndoy, nprob]` -""" -function cal_mTRS_base(arr::AbstractArray{<:Real,3}, dates; +function cal_mTRS_base(arr::AbstractArray{T,N}, dates; + dims=N, probs::Vector=[0.90, 0.95, 0.99, 0.999, 0.9999], - dtype=nothing, - p1::Int=1961, p2::Int=1990, kw...) + dtype=nothing, + p1::Int=1961, p2::Int=1990, kw...) where {T<:Real,N} - mmdd = format_md.(dates) - doy_max = length_unique(mmdd) + mmdd = factor(format_md.(dates)).refs + doy_max = maximum(mmdd) - dim = size(arr) nprob = length(probs) dtype = dtype === nothing ? eltype(arr) : dtype - Q = zeros(dtype, dim[1:2]..., doy_max, nprob) + + dims_r = map(d -> d in dims ? [doy_max, nprob] : size(arr, d), 1:N) + dims_r = cat(dims_r..., dims=1) + Q = zeros(dtype, dims_r...) # constrain date in [p1, p2] years = year.(dates) ind = findall(p1 .<= years .<= p2) - _data = @view arr[:, :, ind] - _dates = @view dates[ind] + _data = selectdim(arr, dims, ind) + _mmdd = @view mmdd[ind] - cal_mTRS_base!(Q, _data, _dates; probs, kw...) + cal_mTRS_base!(Q, _data, _mmdd; dims, probs, kw...) end -cal_mTRS = cal_mTRS_base; - - -""" -Moving Threshold for Heatwaves Definition -$(TYPEDSIGNATURES) -# Arguments - -- `use_mov`: Boolean (default true). - + if `true`, 31*15 values will be used to calculate threshold for each grid; - + if `false`, the input `arr` is smoothed first, then only 15 values will be - used to calculate threshold. - -!!! 必须是完整的年份,不然会出错 - -# References -1. Vogel, M. M., Zscheischler, J., Fischer, E. M., & Seneviratne, S. I. (2020). - Development of Future Heatwaves for Different Hazard Thresholds. Journal of - Geophysical Research: Atmospheres, 125(9). - https://doi.org/10.1029/2019JD032070 -""" -function cal_mTRS_full(arr::AbstractArray{T}, dates; width=15, verbose=true, use_mov=true, - probs=[0.90, 0.95, 0.99, 0.999, 0.9999], kw...) where {T<:Real} +function cal_mTRS_full(arr::AbstractArray{T,N}, dates; + dims=N, + width=15, verbose=true, use_mov=true, + probs=[0.90, 0.95, 0.99, 0.999, 0.9999], kw...) where {T<:Real,N} years = year.(dates) grps = unique(years) @@ -108,32 +67,33 @@ function cal_mTRS_full(arr::AbstractArray{T}, dates; width=15, verbose=true, use YEAR_MIN = minimum(grps) YEAR_MAX = maximum(grps) - mmdd = format_md.(dates) + mmdd = factor(format_md.(dates)).refs mds = unique(mmdd) |> sort doy_max = length(mds) - + # 滑动平均两种不同的做法 if !use_mov printstyled("running: 15d moving average first ... ") - @time arr = movmean(arr, 7; dims=3, FT=Float32) + @time arr = movmean(arr, 7; dims, FT=Float32) end - dim = size(arr) nprob = length(probs) - mTRS_full = zeros(T, dim[1:3]..., nprob) - mTRS = zeros(T, dim[1:2]..., doy_max, nprob) + dim_full = map(d -> d in dims ? [size(arr, d), nprob] : size(arr, d), 1:N) |> x -> vcat(x...) + dim_mTRS = map(d -> d in dims ? [doy_max, nprob] : size(arr, d), 1:N) |> x -> vcat(x...) + + mTRS_full = zeros(T, dim_full...) + mTRS = zeros(T, dim_mTRS...) TRS_head = cal_mTRS_base(arr, dates; p1=YEAR_MIN, p2=YEAR_MIN + width * 2, use_mov, probs, kw...) TRS_tail = cal_mTRS_base(arr, dates; p1=YEAR_MAX - width * 2, p2=YEAR_MAX, use_mov, probs, kw...) - # reset_timer!(to) for year = grps verbose && mod(year, 20) == 0 && println("running [year=$year]") inds_year = years .== year md = @view(mmdd[inds_year]) |> unique - inds = findall(r_in(mds, md)) # this for mTRS + inds_md = findall(r_in(mds, md)) # this for mTRS year_beg = max(year - width, YEAR_MIN) year_end = min(year + width, YEAR_MAX) @@ -143,17 +103,21 @@ function cal_mTRS_full(arr::AbstractArray{T}, dates; width=15, verbose=true, use elseif year >= YEAR_MAX - width _mTRS = TRS_tail else - inds_data = @.(years >= year_beg && year <= year_end) - _data = selectdim(arr, 3, inds_data) - _dates = @view dates[inds_data] - - # mTRS = cal_mTRS_base(_data, _dates; use_mov, probs, kw...) - cal_mTRS_base!(mTRS, _data, _dates; use_mov, probs, kw...) + _data = selectdim(arr, dims, inds_data) + _mmdd = @view mmdd[inds_data] + + cal_mTRS_base!(mTRS, _data, _mmdd; use_mov, probs, kw...) _mTRS = mTRS # 366, 后面统一取ind end - - @views copy!(mTRS_full[:, :, inds_year, :], _mTRS[:, :, inds, :]) + idx_year = ntuple(d -> d in dims ? inds_year : Colon(), N + 1) + idx_md = ntuple(d -> d in dims ? inds_md : Colon(), N + 1) + @views copy!(mTRS_full[idx_year...], _mTRS[idx_md...]) + # 两种方式表现差别不大 + # copy!(selectdim(mTRS_full, dims, inds_year), selectdim(_mTRS, dims, inds_md)) end mTRS_full end + + +end diff --git a/src/Climate/threshold_3d.jl b/src/Climate/threshold_3d.jl new file mode 100644 index 0000000..61bb45f --- /dev/null +++ b/src/Climate/threshold_3d.jl @@ -0,0 +1,159 @@ +export cal_mTRS_base, cal_mTRS_full + + +""" +Moving Threshold for Heatwaves Definition + +$(TYPEDSIGNATURES) + +# Arguments + +- `method_q`: method to calculate quantile, one of `base`, `mapslices`. + `base` is about 3 times faster and reduce used memory in 20 times. + +# References +1. Vogel, M. M., Zscheischler, J., Fischer, E. M., & Seneviratne, S. I. (2020). + Development of Future Heatwaves for Different Hazard Thresholds. Journal of + Geophysical Research: Atmospheres, 125(9). + https://doi.org/10.1029/2019JD032070 +""" +function cal_mTRS_base!(Q::AbstractArray{T}, data::AbstractArray{T}, dates; + probs::Vector=[0.90, 0.95, 0.99, 0.999, 0.9999], + use_mov=true, + halfwin::Int=7, + parallel::Bool=true, + method_q="base", na_rm=false, + ignore...) where {T<:Real} + + mmdd = factor(format_md.(dates)).refs + mds = mmdd |> unique_sort + doy_max = length(mds) + + @inbounds @par parallel for doy = 1:doy_max + ind = filter_mds(mmdd, doy; doy_max, halfwin, use_mov) + + q = @view Q[:, :, doy, :] + x = @view data[:, :, ind] # 这一步耗费内存 + if method_q == "base" + NanQuantile_3d!(q, x; probs, dims=3, na_rm) + # NanQuantile_low!(q, x; probs, dims=3, na_rm) + elseif method_q == "mapslices" + q .= NanQuantile(x; probs, dims=3, na_rm) # mapslices is suppressed for 3d `NanQuantile` + end + end + Q +end + + +""" + $(TYPEDSIGNATURES) + +# Arguments +- `type`: The matching type of the moving `doys`, "md" (default) or "doy". + +# Return +- `TRS`: in the dimension of `[nlat, nlon, ndoy, nprob]` +""" +function cal_mTRS_base(arr::AbstractArray{<:Real,3}, dates; + probs::Vector=[0.90, 0.95, 0.99, 0.999, 0.9999], + dtype=nothing, + p1::Int=1961, p2::Int=1990, kw...) + + mmdd = format_md.(dates) + doy_max = length_unique(mmdd) + + dim = size(arr) + nprob = length(probs) + dtype = dtype === nothing ? eltype(arr) : dtype + Q = zeros(dtype, dim[1:2]..., doy_max, nprob) + + # constrain date in [p1, p2] + years = year.(dates) + ind = findall(p1 .<= years .<= p2) + _data = @view arr[:, :, ind] + _dates = @view dates[ind] + + cal_mTRS_base!(Q, _data, _dates; probs, kw...) +end + +cal_mTRS = cal_mTRS_base; + + +""" +Moving Threshold for Heatwaves Definition + +$(TYPEDSIGNATURES) + +# Arguments + +- `use_mov`: Boolean (default true). + + if `true`, 31*15 values will be used to calculate threshold for each grid; + + if `false`, the input `arr` is smoothed first, then only 15 values will be + used to calculate threshold. + +!!! 必须是完整的年份,不然会出错 + +# References +1. Vogel, M. M., Zscheischler, J., Fischer, E. M., & Seneviratne, S. I. (2020). + Development of Future Heatwaves for Different Hazard Thresholds. Journal of + Geophysical Research: Atmospheres, 125(9). + https://doi.org/10.1029/2019JD032070 +""" +function cal_mTRS_full(arr::AbstractArray{T}, dates; width=15, verbose=true, use_mov=true, + probs=[0.90, 0.95, 0.99, 0.999, 0.9999], kw...) where {T<:Real} + + years = year.(dates) + grps = unique(years) + + YEAR_MIN = minimum(grps) + YEAR_MAX = maximum(grps) + + mmdd = format_md.(dates) + mds = unique(mmdd) |> sort + doy_max = length(mds) + + # 滑动平均两种不同的做法 + if !use_mov + printstyled("running: 15d moving average first ... ") + @time arr = movmean(arr, 7; dims=3, FT=Float32) + end + + dim = size(arr) + nprob = length(probs) + + mTRS_full = zeros(T, dim[1:3]..., nprob) + mTRS = zeros(T, dim[1:2]..., doy_max, nprob) + + TRS_head = cal_mTRS_base(arr, dates; p1=YEAR_MIN, p2=YEAR_MIN + width * 2, use_mov, probs, kw...) + TRS_tail = cal_mTRS_base(arr, dates; p1=YEAR_MAX - width * 2, p2=YEAR_MAX, use_mov, probs, kw...) + + # reset_timer!(to) + for year = grps + verbose && mod(year, 20) == 0 && println("running [year=$year]") + + inds_year = years .== year + md = @view(mmdd[inds_year]) |> unique + inds = findall(r_in(mds, md)) # this for mTRS + + year_beg = max(year - width, YEAR_MIN) + year_end = min(year + width, YEAR_MAX) + # @show year, YEAR_MIN + width, YEAR_MAX - width + if year <= YEAR_MIN + width + _mTRS = TRS_head + elseif year >= YEAR_MAX - width + _mTRS = TRS_tail + else + + inds_data = @.(years >= year_beg && year <= year_end) + _data = selectdim(arr, 3, inds_data) + _dates = @view dates[inds_data] + + # mTRS = cal_mTRS_base(_data, _dates; use_mov, probs, kw...) + cal_mTRS_base!(mTRS, _data, _dates; use_mov, probs, kw...) + _mTRS = mTRS # 366, 后面统一取ind + end + + @views copy!(mTRS_full[:, :, inds_year, :], _mTRS[:, :, inds, :]) + end + mTRS_full +end diff --git a/src/Climate/threshold_vec.jl b/src/Climate/threshold_vec.jl deleted file mode 100644 index 09af772..0000000 --- a/src/Climate/threshold_vec.jl +++ /dev/null @@ -1,128 +0,0 @@ -export Threshold - -module Threshold - -export cal_mTRS_base -using Ipaper - - -function cal_mTRS_base!(Q::AbstractArray{T}, - arr::AbstractArray{T,N}, mmdd; - dims=N, - probs::Vector=[0.90, 0.95, 0.99, 0.999, 0.9999], - parallel=true, halfwin=7, use_mov=true, - kw...) where {T<:Real, N} - - doy_max = maximum(mmdd) - - @inbounds @par parallel for doy = 1:doy_max - ind = filter_mds(mmdd, doy; doy_max, halfwin, use_mov) - # idx = ntuple(i -> i == dims ? ind : Colon(), N) - # ridx = ntuple(i -> i == dims ? doy : Colon(), N + 1) - # x = @view(arr[idx...]) - # q = @view(Q[ridx...]) - x = selectdim(arr, dims, ind) - q = selectdim(Q, dims, doy) - q .= NanQuantile(x; probs, dims) # mapslices - end - Q -end - - -function get_dims_Q(data::AbstractArray{T,N}, doy_max, nprob; dims=N) where {T<:Real,N} - dims_r = map(d -> d in dims ? [doy_max, nprob] : size(data, d), 1:N) - cat(dims_r..., dims=1) -end - -function cal_mTRS_base(arr::AbstractArray{T,N}, dates; - dims=N, - probs::Vector=[0.90, 0.95, 0.99, 0.999, 0.9999], - dtype=nothing, - p1::Int=1961, p2::Int=1990, kw...) where {T<:Real,N} - - mmdd = factor(format_md.(dates)).refs - doy_max = maximum(mmdd) - - nprob = length(probs) - dtype = dtype === nothing ? eltype(arr) : dtype - - dims_r = map(d -> d in dims ? [doy_max, nprob] : size(arr, d), 1:N) - dims_r = cat(dims_r..., dims=1) - Q = zeros(dtype, dims_r...) - - # constrain date in [p1, p2] - years = year.(dates) - ind = findall(p1 .<= years .<= p2) - _data = selectdim(arr, dims, ind) - _mmdd = @view mmdd[ind] - - cal_mTRS_base!(Q, _data, _mmdd; dims, probs, kw...) -end - - - -function cal_mTRS_full(arr::AbstractArray{T,N}, dates; - dims=N, - width=15, verbose=true, use_mov=true, - probs=[0.90, 0.95, 0.99, 0.999, 0.9999], kw...) where {T<:Real,N} - - years = year.(dates) - grps = unique(years) - - YEAR_MIN = minimum(grps) - YEAR_MAX = maximum(grps) - - mmdd = factor(format_md.(dates)).refs - mds = unique(mmdd) |> sort - doy_max = length(mds) - - # 滑动平均两种不同的做法 - if !use_mov - printstyled("running: 15d moving average first ... ") - @time arr = movmean(arr, 7; dims, FT=Float32) - end - - nprob = length(probs) - - dim_full = map(d -> d in dims ? [size(arr, d), nprob] : size(arr, d), 1:N) |> x -> vcat(x...) - dim_mTRS = map(d -> d in dims ? [doy_max, nprob] : size(arr, d), 1:N) |> x -> vcat(x...) - - mTRS_full = zeros(T, dim_full...) - mTRS = zeros(T, dim_mTRS...) - - TRS_head = cal_mTRS_base(arr, dates; p1=YEAR_MIN, p2=YEAR_MIN + width * 2, use_mov, probs, kw...) - TRS_tail = cal_mTRS_base(arr, dates; p1=YEAR_MAX - width * 2, p2=YEAR_MAX, use_mov, probs, kw...) - - for year = grps - verbose && mod(year, 20) == 0 && println("running [year=$year]") - - inds_year = years .== year - md = @view(mmdd[inds_year]) |> unique - inds_md = findall(r_in(mds, md)) # this for mTRS - - year_beg = max(year - width, YEAR_MIN) - year_end = min(year + width, YEAR_MAX) - # @show year, YEAR_MIN + width, YEAR_MAX - width - if year <= YEAR_MIN + width - _mTRS = TRS_head - elseif year >= YEAR_MAX - width - _mTRS = TRS_tail - else - inds_data = @.(years >= year_beg && year <= year_end) - _data = selectdim(arr, dims, inds_data) - _mmdd = @view mmdd[inds_data] - - cal_mTRS_base!(mTRS, _data, _mmdd; use_mov, probs, kw...) - _mTRS = mTRS # 366, 后面统一取ind - end - idx_year = ntuple(d -> d in dims ? inds_year : Colon(), N + 1) - idx_md = ntuple(d -> d in dims ? inds_md : Colon(), N + 1) - @views copy!(mTRS_full[idx_year...], _mTRS[idx_md...]) - # 两种方式表现差别不大 - # copy!(selectdim(mTRS_full, dims, inds_year), selectdim(_mTRS, dims, inds_md)) - end - mTRS_full -end - - -end diff --git a/src/tools.jl b/src/tools.jl index b94918d..a2699f0 100644 --- a/src/tools.jl +++ b/src/tools.jl @@ -42,10 +42,10 @@ length_unique(x::AbstractVector) = length(unique(x)) unique_sort(x) = sort(unique(x)) -seq_along(x) = 1:length(x) +seq_along(x) = seq_len(length(x)) seq_len(n) = 1:n -Range(x) = [minimum(x), maximum(x)] +r_range(x) = [minimum(x), maximum(x)] function obj_size(x) @@ -129,7 +129,8 @@ export which_isna, which_notna, is_empty, not_empty, mean, weighted_mean, weighted_sum, seq_along, seq_len, - Range, + r_range, + nth, length_unique, unique_sort, squeeze, squeeze_tail, squeeze_head, abind, diff --git a/test/Statistics/test_vogel2020.jl b/test/Statistics/test_vogel2020.jl index dcee6b1..3e01bf6 100644 --- a/test/Statistics/test_vogel2020.jl +++ b/test/Statistics/test_vogel2020.jl @@ -19,7 +19,7 @@ lat = nc_read(f, "lat") dates = nc_date(f) @time arr = nc_read(f); -Range(dates) +r_range(dates) # size(arr) # length(dates) # dates[[1, end]] diff --git a/test/Statistics/test_vogel2020.qmd b/test/Statistics/test_vogel2020.qmd index fb7d5d8..f7c7917 100644 --- a/test/Statistics/test_vogel2020.qmd +++ b/test/Statistics/test_vogel2020.qmd @@ -46,7 +46,7 @@ arr2 = replaceMiss(arr) ``` ```{julia} -Range(dates) +r_range(dates) # size(arr) # length(dates) # dates[[1, end]] diff --git a/test/runtests.jl b/test/runtests.jl index 1dae9a0..b57cd02 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,6 +13,7 @@ include("test-string.jl") include("test-list.jl") include("test-date.jl") include("test-r_base.jl") +include("test-tools.jl") include("test-timeit_all.jl") include("test-stat_linreg.jl") diff --git a/test/test-stat_threshold.jl b/test/test-stat_threshold.jl index 3523fe5..85fa996 100644 --- a/test/test-stat_threshold.jl +++ b/test/test-stat_threshold.jl @@ -21,8 +21,8 @@ end @testset "Threshold_nd" begin - kw = (; parallel=false, p1=1961, p2=1965, na_rm=true) - dates = make_date(1961, 1, 1):Day(1):make_date(1970, 12, 31) |> collect + kw = (; parallel=true, p1=1961, p2=1965, na_rm=true) + dates = make_date(1961, 1, 1):Day(1):make_date(2000, 12, 31) #|> collect n = length(dates) set_seed(1) diff --git a/test/test-tools.jl b/test/test-tools.jl new file mode 100644 index 0000000..314b84e --- /dev/null +++ b/test/test-tools.jl @@ -0,0 +1,11 @@ +@testset "tools" begin + @test is_empty([]) + @test is_empty(nothing) + @test !not_empty([]) + @test !not_empty(nothing) + + @test nth(1:10, 2) == 2 + @test seq_along(2:11) == 1:10 + + @test r_range(1:10) == [1, 10] +end