Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
G
geomag-algorithms
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Admin message
Maintenance scheduled for Thursday, April 10, from 5 PM to 6 PM ET.
Show more breadcrumbs
ghsc
National Geomagnetism Program
geomag-algorithms
Commits
55b3ee7e
Commit
55b3ee7e
authored
4 years ago
by
Cain, Payton David
Browse files
Options
Downloads
Patches
Plain Diff
Update documentation, change Transform to BaseModel
parent
fca6f671
No related branches found
Branches containing commit
No related tags found
Tags containing commit
2 merge requests
!146
Release CMO metadata to production
,
!58
Affine running process
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
geomagio/adjusted/Affine.py
+56
-8
56 additions, 8 deletions
geomagio/adjusted/Affine.py
geomagio/adjusted/Metric.py
+11
-2
11 additions, 2 deletions
geomagio/adjusted/Metric.py
geomagio/adjusted/Transform.py
+4
-20
4 additions, 20 deletions
geomagio/adjusted/Transform.py
with
71 additions
and
30 deletions
geomagio/adjusted/Affine.py
+
56
−
8
View file @
55b3ee7e
...
@@ -43,10 +43,8 @@ def filter_iqr(
...
@@ -43,10 +43,8 @@ def filter_iqr(
True that fall within these multiples of quantile ranges.
True that fall within these multiples of quantile ranges.
NOTE: NumPy has a percentile function, but it does not yet handle
NOTE: NumPy has a percentile function, but it does not yet handle
weights. This algorithm was adapted shamelessly from the PyPI
weights. This algorithm was adapted from the PyPI
package wquantiles (https://pypi.org/project/wquantiles/). If
package wquantiles (https://pypi.org/project/wquantiles/)
NumPy should ever implement their own weighted algorithm, we
should use it instead.
Inputs:
Inputs:
series: array of observations to filter
series: array of observations to filter
...
@@ -103,7 +101,7 @@ class Affine(BaseModel):
...
@@ -103,7 +101,7 @@ class Affine(BaseModel):
endtime: end time for matrix creation
endtime: end time for matrix creation
acausal: when True, utilizes readings from after set endtime
acausal: when True, utilizes readings from after set endtime
update_interval: window of time a matrix is representative of
update_interval: window of time a matrix is representative of
transforms: list of methods for matrix calculation
transforms: list of methods for matrix calculation
s
"""
"""
observatory
:
str
=
None
observatory
:
str
=
None
...
@@ -130,17 +128,21 @@ class Affine(BaseModel):
...
@@ -130,17 +128,21 @@ class Affine(BaseModel):
-------
-------
Ms: list of AdjustedMatrix objects created from calculations
Ms: list of AdjustedMatrix objects created from calculations
"""
"""
# default set to create one matrix between starttime and endtime
update_interval
=
self
.
update_interval
or
(
self
.
endtime
-
self
.
starttime
)
update_interval
=
self
.
update_interval
or
(
self
.
endtime
-
self
.
starttime
)
all_readings
=
[
r
for
r
in
readings
if
r
.
valid
]
all_readings
=
[
r
for
r
in
readings
if
r
.
valid
]
Ms
=
[]
Ms
=
[]
time
=
self
.
starttime
time
=
self
.
starttime
epoch_start
=
None
epoch_start
=
None
epoch_end
=
None
epoch_end
=
None
# search for "bad" H values
epochs
=
[
r
.
time
for
r
in
all_readings
if
r
.
get_absolute
(
"
H
"
).
absolute
==
0
]
epochs
=
[
r
.
time
for
r
in
all_readings
if
r
.
get_absolute
(
"
H
"
).
absolute
==
0
]
while
time
<
self
.
endtime
:
while
time
<
self
.
endtime
:
# update epochs for current time
epoch_start
,
epoch_end
=
self
.
get_epochs
(
epoch_start
,
epoch_end
=
self
.
get_epochs
(
epoch_start
=
epoch_start
,
epoch_end
=
epoch_end
,
epochs
=
epochs
,
time
=
time
epoch_start
=
epoch_start
,
epoch_end
=
epoch_end
,
epochs
=
epochs
,
time
=
time
)
)
# utilize readings that occur after or before a bad reading
readings
=
[
readings
=
[
r
r
for
r
in
all_readings
for
r
in
all_readings
...
@@ -148,10 +150,9 @@ class Affine(BaseModel):
...
@@ -148,10 +150,9 @@ class Affine(BaseModel):
or
(
epoch_end
is
None
or
r
.
time
<
epoch_end
)
or
(
epoch_end
is
None
or
r
.
time
<
epoch_end
)
]
]
M
=
self
.
calculate_matrix
(
time
,
readings
)
M
=
self
.
calculate_matrix
(
time
,
readings
)
# if readings are trimmed by bad data, mark the vakid interval
M
.
starttime
=
epoch_start
M
.
starttime
=
epoch_start
M
.
endtime
=
epoch_end
M
.
endtime
=
epoch_end
# increment start_UTC
time
+=
update_interval
time
+=
update_interval
Ms
.
append
(
M
)
Ms
.
append
(
M
)
...
@@ -165,6 +166,20 @@ class Affine(BaseModel):
...
@@ -165,6 +166,20 @@ class Affine(BaseModel):
epochs
:
List
[
float
],
epochs
:
List
[
float
],
time
:
UTCDateTime
,
time
:
UTCDateTime
,
)
->
Tuple
[
float
,
float
]:
)
->
Tuple
[
float
,
float
]:
"""
Updates valid start/end time for a given interval
Attributes
----------
epoch_start: float value signifying start of last valid interval
epoch_end: float value signifying end of last valid interval
epochs: list of floats signifying bad data times
time: current time epoch is being evaluated at
Outputs
-------
epoch_start: float value signifying start of current valid interval
epoch_end: float value signifying end of current valid interval
"""
for
e
in
epochs
:
for
e
in
epochs
:
if
e
>
time
:
if
e
>
time
:
if
epoch_end
is
None
or
e
<
epoch_end
:
if
epoch_end
is
None
or
e
<
epoch_end
:
...
@@ -237,6 +252,11 @@ class Affine(BaseModel):
...
@@ -237,6 +252,11 @@ class Affine(BaseModel):
)
->
AdjustedMatrix
:
)
->
AdjustedMatrix
:
"""
Calculates affine matrix for a given time
"""
Calculates affine matrix for a given time
Attributes
----------
time: time within calculation interval
readings: list of valid readings
Outputs
Outputs
-------
-------
AdjustedMatrix object containing result
AdjustedMatrix object containing result
...
@@ -270,7 +290,6 @@ class Affine(BaseModel):
...
@@ -270,7 +290,6 @@ class Affine(BaseModel):
inputs
=
np
.
dot
(
inputs
=
np
.
dot
(
M
,
np
.
vstack
([
inputs
[
0
],
inputs
[
1
],
inputs
[
2
],
np
.
ones_like
(
inputs
[
0
])])
M
,
np
.
vstack
([
inputs
[
0
],
inputs
[
1
],
inputs
[
2
],
np
.
ones_like
(
inputs
[
0
])])
)[
0
:
3
]
)[
0
:
3
]
metrics
.
append
(
transform
.
metric
)
Ms
.
append
(
M
)
Ms
.
append
(
M
)
# compose affine transform matrices using reverse ordered matrices
# compose affine transform matrices using reverse ordered matrices
...
@@ -298,6 +317,21 @@ class Affine(BaseModel):
...
@@ -298,6 +317,21 @@ class Affine(BaseModel):
def
compute_metrics
(
def
compute_metrics
(
self
,
absolutes
:
List
[
float
],
ordinates
:
List
[
float
],
matrix
:
List
[
float
]
self
,
absolutes
:
List
[
float
],
ordinates
:
List
[
float
],
matrix
:
List
[
float
]
)
->
Tuple
[
List
[
float
],
List
[
float
],
float
,
float
]:
)
->
Tuple
[
List
[
float
],
List
[
float
],
float
,
float
]:
"""
Computes mean absolute error and standard deviation for X, Y, Z, and dF between expected and predicted values.
Attributes
----------
absolutes: X, Y and Z absolutes
ordinates: H, E and Z ordinates
matrix: composed matrix
Outputs
-------
std: standard deviation between expected and predicted XYZ values
mae: mean absolute error between expected and predicted XYZ values
std_df: standard deviation of dF computed from expected and predicted XYZ values
mae_df: mean absolute error of dF computed from expected and predicted XYZ values
"""
# expected values are absolutes
# expected values are absolutes
predicted
=
matrix
@
ordinates
predicted
=
matrix
@
ordinates
# mean absolute erros and standard deviations ignore the 4th row comprison, which is trivial
# mean absolute erros and standard deviations ignore the 4th row comprison, which is trivial
...
@@ -320,6 +354,19 @@ class Affine(BaseModel):
...
@@ -320,6 +354,19 @@ class Affine(BaseModel):
times
:
List
[
UTCDateTime
],
times
:
List
[
UTCDateTime
],
transform
:
Transform
,
transform
:
Transform
,
)
->
np
.
array
:
)
->
np
.
array
:
"""
Attributes
----------
time: time within calculation interval
times: times of valid readings
transform: matrix calculation method
Outputs
-------
weights: array of weights to apply to absolutes/ordinates within calculations
"""
weights
=
transform
.
get_weights
(
time
=
time
.
timestamp
,
times
=
times
)
weights
=
transform
.
get_weights
(
time
=
time
.
timestamp
,
times
=
times
)
# set weights for future observations to zero if not acausal
# set weights for future observations to zero if not acausal
if
not
self
.
acausal
:
if
not
self
.
acausal
:
...
@@ -332,6 +379,7 @@ class Affine(BaseModel):
...
@@ -332,6 +379,7 @@ class Affine(BaseModel):
weights
:
List
[
float
],
weights
:
List
[
float
],
threshold
=
3
,
threshold
=
3
,
)
->
List
[
float
]:
)
->
List
[
float
]:
"""
Filters
"
bad
"
weights generated by unreliable readings
"""
good
=
(
good
=
(
filter_iqr
(
baselines
[
0
],
threshold
=
threshold
,
weights
=
weights
)
filter_iqr
(
baselines
[
0
],
threshold
=
threshold
,
weights
=
weights
)
&
filter_iqr
(
baselines
[
1
],
threshold
=
threshold
,
weights
=
weights
)
&
filter_iqr
(
baselines
[
1
],
threshold
=
threshold
,
weights
=
weights
)
...
...
This diff is collapsed.
Click to expand it.
geomagio/adjusted/Metric.py
+
11
−
2
View file @
55b3ee7e
...
@@ -2,6 +2,15 @@ from pydantic import BaseModel
...
@@ -2,6 +2,15 @@ from pydantic import BaseModel
class
Metric
(
BaseModel
):
class
Metric
(
BaseModel
):
"""
Mean absolute error and standard deviation for a given element
Attributes
----------
element: Channel that metrics are representative of
mae: mean absolute error
std: standard deviation
"""
element
:
str
element
:
str
mae
:
float
mae
:
float
=
None
std
:
float
std
:
float
=
None
This diff is collapsed.
Click to expand it.
geomagio/adjusted/Transform.py
+
4
−
20
View file @
55b3ee7e
import
numpy
as
np
import
numpy
as
np
from
obspy
import
UTCDateTime
from
obspy
import
UTCDateTime
from
pydantic
import
BaseModel
import
scipy.linalg
as
spl
import
scipy.linalg
as
spl
from
typing
import
List
,
Tuple
,
Optional
from
typing
import
List
,
Tuple
,
Optional
class
Transform
(
object
):
class
Transform
(
BaseModel
):
"""
Method for generating an affine matrix.
"""
Method for generating an affine matrix.
Attributes
Attributes
...
@@ -13,13 +14,7 @@ class Transform(object):
...
@@ -13,13 +14,7 @@ class Transform(object):
Defaults to infinite(equal weighting)
Defaults to infinite(equal weighting)
"""
"""
def
__init__
(
self
,
memory
:
float
=
np
.
inf
,
metric
:
float
=
0.0
):
memory
:
Optional
[
float
]
=
None
self
.
memory
=
memory
self
.
metric
=
metric
def
update_metric
(
self
,
sigma
):
# indicates the maximum possible number of lost significant values
self
.
metric
=
np
.
log
(
abs
(
sigma
[
0
]
/
sigma
[
-
1
]))
def
get_weights
(
self
,
times
:
UTCDateTime
,
time
:
int
=
None
)
->
List
[
float
]:
def
get_weights
(
self
,
times
:
UTCDateTime
,
time
:
int
=
None
)
->
List
[
float
]:
"""
"""
...
@@ -32,7 +27,6 @@ class Transform(object):
...
@@ -32,7 +27,6 @@ class Transform(object):
"""
"""
# convert to array of floats
# convert to array of floats
# (allows UTCDateTimes, but not datetime.datetimes)
times
=
np
.
asarray
(
times
).
astype
(
float
)
times
=
np
.
asarray
(
times
).
astype
(
float
)
if
time
is
None
:
if
time
is
None
:
...
@@ -80,7 +74,7 @@ class LeastSq(Transform):
...
@@ -80,7 +74,7 @@ class LeastSq(Transform):
Output
Output
------
------
X, Y and Z absolutes place end to end and transposed
X, Y and Z absolutes place
d
end to end and transposed
"""
"""
return
np
.
vstack
([
absolutes
[
0
],
absolutes
[
1
],
absolutes
[
2
]]).
T
.
ravel
()
return
np
.
vstack
([
absolutes
[
0
],
absolutes
[
1
],
absolutes
[
2
]]).
T
.
ravel
()
...
@@ -181,7 +175,6 @@ class NoConstraints(LeastSq):
...
@@ -181,7 +175,6 @@ class NoConstraints(LeastSq):
# regression matrix M that minimizes L2 norm
# regression matrix M that minimizes L2 norm
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
self
.
update_metric
(
sigma
)
if
rank
<
3
:
if
rank
<
3
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
@@ -227,7 +220,6 @@ class ZRotationShear(LeastSq):
...
@@ -227,7 +220,6 @@ class ZRotationShear(LeastSq):
# regression matrix M that minimizes L2 norm
# regression matrix M that minimizes L2 norm
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
self
.
update_metric
(
sigma
)
if
rank
<
3
:
if
rank
<
3
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
@@ -274,7 +266,6 @@ class ZRotationHscale(LeastSq):
...
@@ -274,7 +266,6 @@ class ZRotationHscale(LeastSq):
# regression matrix M that minimizes L2 norm
# regression matrix M that minimizes L2 norm
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
self
.
update_metric
(
sigma
)
if
rank
<
3
:
if
rank
<
3
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
@@ -321,7 +312,6 @@ class ZRotationHscaleZbaseline(LeastSq):
...
@@ -321,7 +312,6 @@ class ZRotationHscaleZbaseline(LeastSq):
# regression matrix M that minimizes L2 norm
# regression matrix M that minimizes L2 norm
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
self
.
update_metric
(
sigma
)
if
rank
<
3
:
if
rank
<
3
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
@@ -365,7 +355,6 @@ class RotationTranslation3D(SVD):
...
@@ -365,7 +355,6 @@ class RotationTranslation3D(SVD):
# Singular value decomposition, then rotation matrix from L&R eigenvectors
# Singular value decomposition, then rotation matrix from L&R eigenvectors
# (the determinant guarantees a rotation, and not a reflection)
# (the determinant guarantees a rotation, and not a reflection)
U
,
S
,
Vh
=
np
.
linalg
.
svd
(
H
)
U
,
S
,
Vh
=
np
.
linalg
.
svd
(
H
)
self
.
update_metric
(
S
)
if
np
.
sum
(
S
)
<
3
:
if
np
.
sum
(
S
)
<
3
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
@@ -415,7 +404,6 @@ class Rescale3D(LeastSq):
...
@@ -415,7 +404,6 @@ class Rescale3D(LeastSq):
# regression matrix M that minimizes L2 norm
# regression matrix M that minimizes L2 norm
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
self
.
update_metric
(
sigma
)
if
rank
<
3
:
if
rank
<
3
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
@@ -470,7 +458,6 @@ class TranslateOrigins(LeastSq):
...
@@ -470,7 +458,6 @@ class TranslateOrigins(LeastSq):
# regression matrix M that minimizes L2 norm
# regression matrix M that minimizes L2 norm
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
self
.
update_metric
(
sigma
)
if
rank
<
3
:
if
rank
<
3
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
@@ -510,7 +497,6 @@ class ShearYZ(LeastSq):
...
@@ -510,7 +497,6 @@ class ShearYZ(LeastSq):
abs_stacked
=
self
.
get_weighted_values
(
values
=
abs_stacked
,
weights
=
weights
)
abs_stacked
=
self
.
get_weighted_values
(
values
=
abs_stacked
,
weights
=
weights
)
# regression matrix M that minimizes L2 norm
# regression matrix M that minimizes L2 norm
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
self
.
update_metric
(
sigma
)
if
rank
<
3
:
if
rank
<
3
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
@@ -559,7 +545,6 @@ class RotationTranslationXY(SVD):
...
@@ -559,7 +545,6 @@ class RotationTranslationXY(SVD):
# Singular value decomposition, then rotation matrix from L&R eigenvectors
# Singular value decomposition, then rotation matrix from L&R eigenvectors
# (the determinant guarantees a rotation, and not a reflection)
# (the determinant guarantees a rotation, and not a reflection)
U
,
S
,
Vh
=
np
.
linalg
.
svd
(
H
)
U
,
S
,
Vh
=
np
.
linalg
.
svd
(
H
)
self
.
update_metric
(
S
)
if
np
.
sum
(
S
)
<
2
:
if
np
.
sum
(
S
)
<
2
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
@@ -642,7 +627,6 @@ class QRFactorization(SVD):
...
@@ -642,7 +627,6 @@ class QRFactorization(SVD):
# regression matrix M that minimizes L2 norm
# regression matrix M that minimizes L2 norm
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
M_r
,
res
,
rank
,
sigma
=
spl
.
lstsq
(
ord_stacked
.
T
,
abs_stacked
.
T
)
self
.
update_metric
(
sigma
)
if
rank
<
2
:
if
rank
<
2
:
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
print
(
"
Poorly conditioned or singular matrix, returning NaNs
"
)
return
np
.
nan
*
np
.
ones
((
4
,
4
))
return
np
.
nan
*
np
.
ones
((
4
,
4
))
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment