Skip to content
This repository was archived by the owner on Mar 11, 2022. It is now read-only.

Commit fc03843

Browse files
authored
Add ScaledArray, RotatingTimeValue and settime (#23)
1 parent bba59a4 commit fc03843

File tree

14 files changed

+641
-128
lines changed

14 files changed

+641
-128
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
88
CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193"
99
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
1010
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
11+
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
1112
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1213
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
1314
Missings = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"

src/DiffinDiffsBase.jl

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ using Base: @propagate_inbounds
44
using CSV
55
using CodecZlib: GzipDecompressorStream
66
using Combinatorics: combinations
7+
using DataAPI
78
using DataAPI: refarray, refpool, invrefpool
9+
using Dates: Period, TimeType
810
using LinearAlgebra: Diagonal
911
using MacroTools: @capture, isexpr, postwalk
1012
using Missings: allowmissing, disallowmissing
@@ -15,23 +17,23 @@ using StatsFuns: tdistccdf, tdistinvcdf
1517
@reexport using StatsModels
1618
using StatsModels: Schema
1719
using Tables
18-
using Tables: AbstractColumns, table, istable, columnnames, getcolumn
20+
using Tables: AbstractColumns, istable, columnnames, getcolumn
1921

20-
import Base: ==, show, parent, view, diff
22+
import Base: ==, +, -, *, isless, show, parent, view, diff
2123
import Base: eltype, firstindex, lastindex, getindex, iterate, length, sym_in
2224
import StatsBase: coef, vcov, confint, nobs, dof_residual, responsename, coefnames, weights,
2325
coeftable
2426
import StatsModels: concrete_term, schema, termvars, lag, lead
2527

26-
const TimeType = Int
27-
2828
# Reexport objects from StatsBase
2929
export coef, vcov, stderror, confint, nobs, dof_residual, responsename, coefnames, weights,
3030
coeftable
3131

3232
export cb,
3333
,
3434
exampledata,
35+
RotatingTimeValue,
36+
rotatingtime,
3537

3638
VecColumnTable,
3739
VecColsRow,
@@ -41,6 +43,10 @@ export cb,
4143
apply_and,
4244
TableIndexedMatrix,
4345

46+
ScaledArray,
47+
ScaledVector,
48+
ScaledMatrix,
49+
4450
TreatmentSharpness,
4551
SharpDesign,
4652
sharp,
@@ -72,6 +78,7 @@ export cb,
7278

7379
findcell,
7480
cellrows,
81+
settime,
7582
PanelStructure,
7683
setpanel,
7784
findlag!,
@@ -120,6 +127,7 @@ export cb,
120127

121128
include("utils.jl")
122129
include("tables.jl")
130+
include("ScaledArrays.jl")
123131
include("treatments.jl")
124132
include("parallels.jl")
125133
include("terms.jl")

src/ScaledArrays.jl

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
const DEFAULT_REF_TYPE = Int32
2+
3+
# A wrapper only used for resolving method ambiguity when constructing ScaledArray
4+
mutable struct RefArray{R}
5+
a::R
6+
end
7+
8+
"""
9+
ScaledArray{T,N,RA,S,P} <: AbstractArray{T,N}
10+
11+
An array type that stores data as indices of a range.
12+
13+
# Fields
14+
- `refs::RA<:AbstractArray{<:Any, N}`: an array of indices.
15+
- `start::T`: the starting value of the range.
16+
- `step::S`: the step size of the range.
17+
- `stop::T`: the stopping value of the range.
18+
- `pool::P<:AbstractRange{T}`: a range that covers all possible values stored by the array.
19+
"""
20+
mutable struct ScaledArray{T,N,RA,S,P} <: AbstractArray{T,N}
21+
refs::RA
22+
start::T
23+
step::S
24+
stop::T
25+
pool::P
26+
ScaledArray{T,N,RA,S,P}(rs::RefArray{RA}, start::T, step::S, stop::T,
27+
pool::P=start:step:stop) where
28+
{T, N, RA<:AbstractArray{<:Any, N}, S, P<:AbstractRange{T}} =
29+
new{T,N,RA,S,P}(rs.a, start, step, stop, pool)
30+
end
31+
32+
ScaledArray(rs::RefArray{RA}, start::T, step::S, stop::T, pool::P=start:step:stop) where
33+
{T,RA,S,P} = ScaledArray{T,ndims(RA),RA,S,P}(rs, start, step, stop, pool)
34+
35+
const ScaledVector{T} = ScaledArray{T,1}
36+
const ScaledMatrix{T} = ScaledArray{T,2}
37+
38+
function _validmin(min, xmin, isstart::Bool)
39+
if min === nothing
40+
min = xmin
41+
elseif min > xmin
42+
str = isstart ? "start =" : "stop ="
43+
throw(ArgumentError("$str $min is greater than the allowed minimum element $xmin"))
44+
end
45+
return min
46+
end
47+
48+
function _validmax(max, xmax, isstart::Bool)
49+
if max === nothing
50+
max = xmax
51+
elseif max < xmax
52+
str = isstart ? "start =" : "stop ="
53+
throw(ArgumentError("$str $max is smaller than the allowed maximum element $xmax"))
54+
end
55+
return max
56+
end
57+
58+
function validstartstepstop(x::AbstractArray, start, step, stop, usepool)
59+
step === nothing && throw(ArgumentError("step cannot be nothing"))
60+
pool = DataAPI.refpool(x)
61+
xmin, xmax = usepool && pool !== nothing ? extrema(pool) : extrema(x)
62+
applicable(+, xmin, step) || throw(ArgumentError(
63+
"step of type $(typeof(step)) does not match array with element type $(eltype(x))"))
64+
if xmin + step > xmin
65+
start = _validmin(start, xmin, true)
66+
stop = _validmax(stop, xmax, false)
67+
elseif xmin + step < xmin
68+
start = _validmax(start, xmax, true)
69+
stop = _validmin(stop, xmin, false)
70+
else
71+
throw(ArgumentError("step cannot be zero"))
72+
end
73+
T = promote_type(eltype(x), eltype(start:step:stop))
74+
return convert(T, start), convert(T, stop)
75+
end
76+
77+
function _scaledlabel(x::AbstractArray, step, reftype::Type{<:Signed}=DEFAULT_REF_TYPE;
78+
start=nothing, stop=nothing, usepool::Bool=true)
79+
start, stop = validstartstepstop(x, start, step, stop, usepool)
80+
pool = start:step:stop
81+
while typemax(reftype) < length(pool)
82+
reftype = widen(reftype)
83+
end
84+
refs = similar(x, reftype)
85+
@inbounds for i in eachindex(refs)
86+
refs[i] = length(start:step:x[i])
87+
end
88+
return refs, start, step, stop
89+
end
90+
91+
function ScaledArray(x::AbstractArray, reftype::Type, start, step, stop, usepool::Bool=true)
92+
refs, start, step, stop = _scaledlabel(x, step, reftype; start=start, stop=stop, usepool=usepool)
93+
return ScaledArray(RefArray(refs), start, step, stop)
94+
end
95+
96+
function ScaledArray(sa::ScaledArray, reftype::Type, start, step, stop, usepool::Bool=true)
97+
if step !== nothing && step != sa.step
98+
refs, start, step, stop = _scaledlabel(sa, step, reftype; start=start, stop=stop, usepool=usepool)
99+
return ScaledArray(RefArray(refs), start, step, stop)
100+
else
101+
step = sa.step
102+
start, stop = validstartstepstop(sa, start, step, stop, usepool)
103+
refs = similar(sa.refs, reftype)
104+
if start == sa.start
105+
copy!(refs, sa.refs)
106+
elseif start < sa.start && start < stop || start > sa.start && start > stop
107+
offset = length(start:step:sa.start) - 1
108+
refs .= sa.refs .+ offset
109+
else
110+
offset = length(sa.start:step:start) - 1
111+
refs .= sa.refs .- offset
112+
end
113+
return ScaledArray(RefArray(refs), start, step, stop)
114+
end
115+
end
116+
117+
"""
118+
ScaledArray(x::AbstractArray, start, step[, stop]; reftype=Int32, usepool=true)
119+
ScaledArray(x::AbstractArray, step; reftype=Int32, start, stop, usepool=true)
120+
121+
Construct a `ScaledArray` from `x` given the `step` size.
122+
If `start` or `stop` is not specified, it will be chosen based on the extrema of `x`.
123+
124+
# Keywords
125+
- `reftype::Type=Int32`: the element type of field `refs`.
126+
- `usepool::Bool=true`: find extrema of `x` based on `DataAPI.refpool`.
127+
"""
128+
ScaledArray(x::AbstractArray, start, step, stop=nothing;
129+
reftype::Type=DEFAULT_REF_TYPE, usepool::Bool=true) =
130+
ScaledArray(x, reftype, start, step, stop, usepool)
131+
132+
ScaledArray(sa::ScaledArray, start, step, stop=nothing;
133+
reftype::Type=eltype(refarray(sa)), usepool::Bool=true) =
134+
ScaledArray(sa, reftype, start, step, stop, usepool)
135+
136+
ScaledArray(x::AbstractArray, step; reftype::Type=DEFAULT_REF_TYPE,
137+
start=nothing, stop=nothing, usepool::Bool=true) =
138+
ScaledArray(x, reftype, start, step, stop, usepool)
139+
140+
ScaledArray(sa::ScaledArray, step=nothing; reftype::Type=eltype(refarray(sa)),
141+
start=nothing, stop=nothing, usepool::Bool=true) =
142+
ScaledArray(sa, reftype, start, step, stop, usepool)
143+
144+
Base.size(sa::ScaledArray) = size(sa.refs)
145+
Base.IndexStyle(::Type{<:ScaledArray{T,N,RA}}) where {T,N,RA} = IndexStyle(RA)
146+
147+
DataAPI.refarray(sa::ScaledArray) = sa.refs
148+
DataAPI.refvalue(sa::ScaledArray, n::Integer) = getindex(DataAPI.refpool(sa), n)
149+
DataAPI.refpool(sa::ScaledArray) = sa.pool
150+
151+
DataAPI.refarray(ssa::SubArray{<:Any, <:Any, <:ScaledArray}) =
152+
view(parent(ssa).refs, ssa.indices...)
153+
DataAPI.refvalue(ssa::SubArray{<:Any, <:Any, <:ScaledArray}, n::Integer) =
154+
DataAPI.refvalue(parent(ssa), n)
155+
DataAPI.refpool(ssa::SubArray{<:Any, <:Any, <:ScaledArray}) =
156+
DataAPI.refpool(parent(ssa))
157+
158+
@inline function Base.getindex(sa::ScaledArray, i::Int)
159+
refs = DataAPI.refarray(sa)
160+
@boundscheck checkbounds(refs, i)
161+
@inbounds n = refs[i]
162+
pool = DataAPI.refpool(sa)
163+
@boundscheck checkbounds(pool, n)
164+
return @inbounds pool[n]
165+
end
166+
167+
@inline function Base.getindex(sa::ScaledArray, I...)
168+
refs = DataAPI.refarray(sa)
169+
@boundscheck checkbounds(refs, I...)
170+
@inbounds ns = refs[I...]
171+
pool = DataAPI.refpool(sa)
172+
@boundscheck checkbounds(pool, ns)
173+
return @inbounds pool[ns]
174+
end
175+
176+
function Base.:(==)(x::ScaledArray, y::ScaledArray)
177+
size(x) == size(y) || return false
178+
x.start == y.start && x.step == y.step && return x.refs == y.refs
179+
eq = true
180+
for (p, q) in zip(x, y)
181+
# missing could arise
182+
veq = p == q
183+
isequal(veq, false) && return false
184+
eq &= veq
185+
end
186+
return eq
187+
end

0 commit comments

Comments
 (0)