spatial_stats 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +23 -15
- data/lib/spatial_stats/local/multivariate_geary.rb +22 -18
- data/lib/spatial_stats/local/stat.rb +54 -82
- data/lib/spatial_stats/version.rb +1 -1
- data/lib/spatial_stats/weights/weights_matrix.rb +13 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d63578b1952570f2de9a3ab045c8a977988960c2942c73416b86de6b228a091
|
4
|
+
data.tar.gz: 591927bf3c2fd3d7723b7e155bbae6a2c73313b45c749c6ab552e445e5f3b609
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfe901b03685c5d66539793d7446738c49b0cdaae3022559544620962aa9d63e4d668d0177b8dc3c44cff797ce5c8ebed8fdeba87a52ec28ebdf0d02b69625b3
|
7
|
+
data.tar.gz: 3cfde56bb984bfbf3ab83bb89e02797a24eb47b1a8387e695890127ee5711f185a114f300906f96728c0e51996f223091f0a9455713f8fb3c3ff34b8285615da
|
data/README.md
CHANGED
@@ -58,8 +58,6 @@ weights = SpatialStats::Weights::Distant.idw_knn(scope, :geom, 5)
|
|
58
58
|
|
59
59
|
Weight matrices can be defined by a hash that describes each key's neighbor and weight.
|
60
60
|
|
61
|
-
Note: Currently, the keys must be numeric.
|
62
|
-
|
63
61
|
Example: Define WeightsMatrix and get the matrix in row_standardized format.
|
64
62
|
|
65
63
|
```ruby
|
@@ -70,30 +68,40 @@ weights = {
|
|
70
68
|
4 => [{ id: 1, weight: 1 }, { id: 3, weight: 1 }]
|
71
69
|
}
|
72
70
|
keys = weights.keys
|
73
|
-
wm = SpatialStats::Weights::WeightsMatrix.new(
|
71
|
+
wm = SpatialStats::Weights::WeightsMatrix.new(weights)
|
74
72
|
# => #<SpatialStats::Weights::WeightsMatrix:0x0000561e205677c0 @keys=[1, 2, 3, 4], @weights={1=>[{:id=>2, :weight=>1}, {:id=>4, :weight=>1}], 2=>[{:id=>1, :weight=>1}], 3=>[{:id=>4, :weight=>1}], 4=>[{:id=>1, :weight=>1}, {:id=>3, :weight=>1}]}, @n=4>
|
75
73
|
|
76
|
-
wm.
|
77
|
-
# =>
|
78
|
-
|
79
|
-
|
80
|
-
# [
|
81
|
-
#
|
74
|
+
wm = wm.standardize
|
75
|
+
# => #<SpatialStats::Weights::WeightsMatrix:0x0000561e205677c0 @keys=[1, 2, 3, 4], @weights={1=>[{:id=>2, :weight=>0.5}, {:id=>4, :weight=>0.5}], 2=>[{:id=>1, :weight=>1}], 3=>[{:id=>4, :weight=>1}], 4=>[{:id=>1, :weight=>0.5}, {:id=>3, :weight=>0.5}]}, @n=4>
|
76
|
+
|
77
|
+
wm.dense
|
78
|
+
# => Numo::DFloat[
|
79
|
+
# [0, 0.5, 0, 0.5],
|
80
|
+
# [1, 0, 0, 0],
|
81
|
+
# [0, 0, 0, 1],
|
82
|
+
# [0.5, 0, 0.5, 0]
|
83
|
+
# ]
|
84
|
+
|
85
|
+
wm.sparse
|
86
|
+
# => #<SpatialStats::Weights::CSRMatrix @m=4, @n=4, @nnz=6>
|
82
87
|
```
|
83
88
|
|
84
89
|
### Lagged Variables
|
85
90
|
|
86
|
-
Spatially lagged variables can be computed with
|
91
|
+
Spatially lagged variables can be computed with weights matrix and 1-D vector (`Array`).
|
87
92
|
|
88
93
|
#### Compute a Lagged Variable
|
89
94
|
|
90
95
|
```ruby
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
96
|
+
weights = {
|
97
|
+
1 => [{ id: 2, weight: 1 }, { id: 4, weight: 1 }],
|
98
|
+
2 => [{ id: 1, weight: 1 }],
|
99
|
+
3 => [{ id: 4, weight: 1 }],
|
100
|
+
4 => [{ id: 1, weight: 1 }, { id: 3, weight: 1 }]
|
101
|
+
}
|
102
|
+
wm = SpatialStats::Weights::WeightsMatrix.new(weights).standardize
|
95
103
|
vec = [1, 2, 3, 4]
|
96
|
-
lagged_var = SpatialStats::Utils::Lag.neighbor_sum(
|
104
|
+
lagged_var = SpatialStats::Utils::Lag.neighbor_sum(wm, vec)
|
97
105
|
# => [3.0, 1.0, 4.0, 2.0]
|
98
106
|
```
|
99
107
|
|
@@ -60,35 +60,39 @@ module SpatialStats
|
|
60
60
|
# of indices, which will return a list of new orders for the fields.
|
61
61
|
# They will then be shuffled corresponding to the new indices.
|
62
62
|
rng = gen_rng(seed)
|
63
|
-
|
64
|
-
indices = (0..(n - 1)).to_a
|
65
|
-
shuffles = crand(indices, permutations, rng)
|
63
|
+
rids = crand(permutations, rng)
|
66
64
|
|
65
|
+
n_1 = weights.n - 1
|
66
|
+
sparse = weights.sparse
|
67
|
+
row_index = sparse.row_index
|
68
|
+
ws = sparse.values
|
69
|
+
wc = weights.wc
|
67
70
|
stat_orig = stat
|
68
|
-
rs = [0] * n
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
ids = (0..n_1).to_a
|
73
|
+
observations = Array.new(weights.n)
|
74
|
+
(0..n_1).each do |idx|
|
75
|
+
idsi = ids.dup
|
76
|
+
idsi.delete_at(idx)
|
77
|
+
idsi.shuffle!(random: rng)
|
78
|
+
idsi = Numo::Int32.cast(idsi)
|
79
|
+
sample = rids[idsi[rids[true, 0..wc[idx] - 1]]]
|
76
80
|
|
81
|
+
# account for case where there are no neighbors
|
77
82
|
row_range = row_index[idx]..(row_index[idx + 1] - 1)
|
78
83
|
if row_range.size.zero?
|
79
|
-
|
80
|
-
idx += 1
|
84
|
+
observations[idx] = permutations
|
81
85
|
next
|
82
86
|
end
|
83
|
-
wi = Numo::DFloat.cast(ws[row_range])
|
84
87
|
|
85
|
-
|
86
|
-
stat_i_new = mc_i(wi,
|
87
|
-
|
88
|
-
idx
|
88
|
+
wi = Numo::DFloat.cast(ws[row_range])
|
89
|
+
stat_i_new = mc_i(wi, sample, idx)
|
90
|
+
stat_i_orig = stat_orig[idx]
|
91
|
+
observations[idx] = mc_observation_calc(stat_i_orig, stat_i_new,
|
92
|
+
permutations)
|
89
93
|
end
|
90
94
|
|
91
|
-
|
95
|
+
observations.map do |ri|
|
92
96
|
(ri + 1.0) / (permutations + 1.0)
|
93
97
|
end
|
94
98
|
end
|
@@ -42,12 +42,12 @@ module SpatialStats
|
|
42
42
|
|
43
43
|
##
|
44
44
|
# Conditional randomization algorithm used in permutation testing.
|
45
|
-
#
|
46
|
-
#
|
47
|
-
# num_neighbors is the number of neighbors for that observation.
|
45
|
+
# Returns a matrix with permuted index values that will be used for
|
46
|
+
# selecting values from the original data set.
|
48
47
|
#
|
49
|
-
# The
|
50
|
-
#
|
48
|
+
# The width of the matrix is the max number of neighbors + 1
|
49
|
+
# which is way less than it would be if the original vector
|
50
|
+
# was shuffled in full.
|
51
51
|
#
|
52
52
|
# This is super important because most weight matrices are very
|
53
53
|
# sparse so the amount of shuffling/multiplication that is done
|
@@ -55,9 +55,9 @@ module SpatialStats
|
|
55
55
|
#
|
56
56
|
# @see https://github.com/pysal/esda/blob/master/esda/moran.py#L893
|
57
57
|
#
|
58
|
-
# @return [
|
58
|
+
# @return [Numo::Int32] matrix of shape perms x wc_max + 1
|
59
59
|
#
|
60
|
-
def crand(
|
60
|
+
def crand(permutations, rng)
|
61
61
|
# basing this off the ESDA method
|
62
62
|
# need to get k for max_neighbors
|
63
63
|
# and wc for cardinalities of each item
|
@@ -68,32 +68,13 @@ module SpatialStats
|
|
68
68
|
# entry not the entire list of permutations for each entry.
|
69
69
|
n_1 = weights.n - 1
|
70
70
|
|
71
|
-
sparse = weights.sparse
|
72
|
-
row_index = sparse.row_index
|
73
|
-
|
74
71
|
# weight counts
|
75
|
-
wc =
|
76
|
-
k = 0
|
77
|
-
(0..n_1).each do |idx|
|
78
|
-
wc[idx] = row_index[idx + 1] - row_index[idx]
|
79
|
-
end
|
80
|
-
|
72
|
+
wc = weights.wc
|
81
73
|
k = wc.max + 1
|
82
74
|
prange = (0..permutations - 1).to_a
|
83
75
|
|
84
|
-
arr = Numo::DFloat.cast(arr)
|
85
|
-
|
86
|
-
ids = (0..n_1).to_a
|
87
76
|
ids_perm = (0..n_1 - 1).to_a
|
88
|
-
|
89
|
-
|
90
|
-
(0..n_1).map do |idx|
|
91
|
-
idsi = ids.dup
|
92
|
-
idsi.delete_at(idx)
|
93
|
-
idsi.shuffle!(random: rng)
|
94
|
-
idsi = Numo::Int32.cast(idsi)
|
95
|
-
arr[idsi[rids[true, 0..wc[idx] - 1]]]
|
96
|
-
end
|
77
|
+
Numo::Int32.cast(prange.map { ids_perm.sample(k, random: rng) })
|
97
78
|
end
|
98
79
|
|
99
80
|
##
|
@@ -114,48 +95,40 @@ module SpatialStats
|
|
114
95
|
# its neighbors. Then we will only test for that item instead
|
115
96
|
# of the entire set. This will be done for each item.
|
116
97
|
rng = gen_rng(seed)
|
117
|
-
|
98
|
+
rids = crand(permutations, rng)
|
118
99
|
|
119
|
-
|
120
|
-
|
100
|
+
n_1 = weights.n - 1
|
101
|
+
sparse = weights.sparse
|
102
|
+
row_index = sparse.row_index
|
103
|
+
ws = sparse.values
|
104
|
+
wc = weights.wc
|
121
105
|
stat_orig = stat
|
122
|
-
rs = [0] * n
|
123
|
-
|
124
|
-
row_index = weights.sparse.row_index
|
125
|
-
ws = weights.sparse.values
|
126
106
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
107
|
+
arr = Numo::DFloat.cast(x)
|
108
|
+
ids = (0..n_1).to_a
|
109
|
+
observations = Array.new(weights.n)
|
110
|
+
(0..n_1).each do |idx|
|
111
|
+
idsi = ids.dup
|
112
|
+
idsi.delete_at(idx)
|
113
|
+
idsi.shuffle!(random: rng)
|
114
|
+
idsi = Numo::Int32.cast(idsi)
|
115
|
+
sample = arr[idsi[rids[true, 0..wc[idx] - 1]]]
|
133
116
|
|
134
117
|
# account for case where there are no neighbors
|
135
|
-
# the way Numo handles negative ranges, it returns the max
|
136
|
-
# so there will be a len 0 z array being multiplied by a
|
137
|
-
# max_neighbor width permutation matrix.
|
138
|
-
# Need to skip.
|
139
118
|
row_range = row_index[idx]..(row_index[idx + 1] - 1)
|
140
119
|
if row_range.size.zero?
|
141
|
-
|
142
|
-
idx += 1
|
120
|
+
observations[idx] = permutations
|
143
121
|
next
|
144
122
|
end
|
145
|
-
wi = Numo::DFloat.cast(ws[row_range])
|
146
|
-
stat_i_new = mc_i(wi, shuffles[idx], idx)
|
147
123
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
# (stat_i_new <= stat_i_orig).count
|
154
|
-
# end
|
155
|
-
idx += 1
|
124
|
+
wi = Numo::DFloat.cast(ws[row_range])
|
125
|
+
stat_i_new = mc_i(wi, sample, idx)
|
126
|
+
stat_i_orig = stat_orig[idx]
|
127
|
+
observations[idx] = mc_observation_calc(stat_i_orig, stat_i_new,
|
128
|
+
permutations)
|
156
129
|
end
|
157
130
|
|
158
|
-
|
131
|
+
observations.map do |ri|
|
159
132
|
(ri + 1.0) / (permutations + 1.0)
|
160
133
|
end
|
161
134
|
end
|
@@ -174,41 +147,40 @@ module SpatialStats
|
|
174
147
|
# @return [Array] of p-values
|
175
148
|
def mc_bv(permutations, seed)
|
176
149
|
rng = gen_rng(seed)
|
177
|
-
|
178
|
-
n = weights.n
|
150
|
+
rids = crand(permutations, rng)
|
179
151
|
|
152
|
+
n_1 = weights.n - 1
|
153
|
+
sparse = weights.sparse
|
154
|
+
row_index = sparse.row_index
|
155
|
+
ws = sparse.values
|
156
|
+
wc = weights.wc
|
180
157
|
stat_orig = stat
|
181
|
-
rs = [0] * n
|
182
|
-
|
183
|
-
row_index = weights.sparse.row_index
|
184
|
-
ws = weights.sparse.values
|
185
158
|
|
186
|
-
|
187
|
-
|
188
|
-
|
159
|
+
arr = Numo::DFloat.cast(y)
|
160
|
+
ids = (0..n_1).to_a
|
161
|
+
observations = Array.new(weights.n)
|
162
|
+
(0..n_1).each do |idx|
|
163
|
+
idsi = ids.dup
|
164
|
+
idsi.delete_at(idx)
|
165
|
+
idsi.shuffle!(random: rng)
|
166
|
+
idsi = Numo::Int32.cast(idsi)
|
167
|
+
sample = arr[idsi[rids[true, 0..wc[idx] - 1]]]
|
189
168
|
|
169
|
+
# account for case where there are no neighbors
|
190
170
|
row_range = row_index[idx]..(row_index[idx + 1] - 1)
|
191
171
|
if row_range.size.zero?
|
192
|
-
|
193
|
-
idx += 1
|
172
|
+
observations[idx] = permutations
|
194
173
|
next
|
195
174
|
end
|
196
|
-
wi = Numo::DFloat.cast(ws[row_range])
|
197
|
-
|
198
|
-
stat_i_new = mc_i(wi, shuffles[idx], idx)
|
199
|
-
|
200
|
-
rs[idx] = mc_observation_calc(stat_i_orig, stat_i_new,
|
201
|
-
permutations)
|
202
|
-
# if stat_i_orig.positive?
|
203
|
-
# (stat_i_new >= stat_i_orig).count
|
204
|
-
# else
|
205
|
-
# (stat_i_new <= stat_i_orig).count
|
206
|
-
# end
|
207
175
|
|
208
|
-
|
176
|
+
wi = Numo::DFloat.cast(ws[row_range])
|
177
|
+
stat_i_new = mc_i(wi, sample, idx)
|
178
|
+
stat_i_orig = stat_orig[idx]
|
179
|
+
observations[idx] = mc_observation_calc(stat_i_orig, stat_i_new,
|
180
|
+
permutations)
|
209
181
|
end
|
210
182
|
|
211
|
-
|
183
|
+
observations.map do |ri|
|
212
184
|
(ri + 1.0) / (permutations + 1.0)
|
213
185
|
end
|
214
186
|
end
|
@@ -58,6 +58,19 @@ module SpatialStats
|
|
58
58
|
@sparse ||= CSRMatrix.new(dense.to_a.flatten, n, n)
|
59
59
|
end
|
60
60
|
|
61
|
+
##
|
62
|
+
# Compute the cardinalities of each neighbor into an array
|
63
|
+
#
|
64
|
+
# @return [Array]
|
65
|
+
def wc
|
66
|
+
@wc ||= begin
|
67
|
+
row_index = sparse.row_index
|
68
|
+
(0..n - 1).map do |idx|
|
69
|
+
row_index[idx + 1] - row_index[idx]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
61
74
|
##
|
62
75
|
# Row standardized version of the weights matrix.
|
63
76
|
# Will return a new version of the weights matrix with standardized
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spatial_stats
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith Doggett
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: numo-narray
|