Skip to content

Commit 4b049f7

Browse files
author
Mika Sorvoja
authored
Add WofE CLI functions (#475)
1 parent b8ae3de commit 4b049f7

File tree

2 files changed

+180
-4
lines changed

2 files changed

+180
-4
lines changed

eis_toolkit/cli.py

+175-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import json
88
import os
99
from enum import Enum
10+
from itertools import zip_longest
1011
from pathlib import Path
1112

1213
import geopandas as gpd
@@ -332,6 +333,15 @@ class KerasRegressorMetrics(str, Enum):
332333
mae = "mae"
333334

334335

336+
class WeightsType(str, Enum):
337+
"""Weights type for WofE."""
338+
339+
unique = "unique"
340+
categorical = "categorical"
341+
ascending = "ascending"
342+
descending = "descending"
343+
344+
335345
INPUT_FILE_OPTION = Annotated[
336346
Path,
337347
typer.Option(
@@ -344,6 +354,18 @@ class KerasRegressorMetrics(str, Enum):
344354
),
345355
]
346356

357+
INPUT_FILES_OPTION = Annotated[
358+
List[Path],
359+
typer.Option(
360+
exists=True,
361+
file_okay=True,
362+
dir_okay=False,
363+
writable=False,
364+
readable=True,
365+
resolve_path=True,
366+
),
367+
]
368+
347369
INPUT_FILES_ARGUMENT = Annotated[
348370
List[Path],
349371
typer.Argument(
@@ -3065,7 +3087,159 @@ def gamma_overlay_cli(input_rasters: INPUT_FILES_ARGUMENT, output_raster: OUTPUT
30653087

30663088

30673089
# WOFE
3068-
# TODO
3090+
@app.command()
3091+
def weights_of_evidence_calculate_weights_cli(
3092+
input_raster: INPUT_FILE_OPTION,
3093+
input_vector: INPUT_FILE_OPTION,
3094+
output_dir: OUTPUT_DIR_OPTION,
3095+
raster_nodata: Optional[float] = None,
3096+
weights_type: Annotated[WeightsType, typer.Option(case_sensitive=False)] = WeightsType.unique,
3097+
studentized_contrast_threshold: float = 1,
3098+
arrays_to_generate: Annotated[Optional[List[str]], typer.Option()] = None,
3099+
):
3100+
"""
3101+
Calculate weights of spatial associations.
3102+
3103+
Parameter --studentized-contrast-threshold is used with 'categorical', 'ascending' and 'descending' weight types.
3104+
3105+
Parameter --arrays-to-generate controls which columns in the weights dataframe are returned as arrays. All column
3106+
names in the produced weights_df are valid choices. The available columns for "unique" weights_type are "Class",
3107+
"Pixel count", "Deposit count", "W+", "S_W+", "W-", "S_W-", "Contrast", "S_Contrast", and "Studentized contrast".
3108+
For other weights types, additional available column names are "Generalized class", "Generalized W+", and
3109+
"Generalized S_W+". Defaults to ["Class", "W+", "S_W+] for "unique" weights_type and ["Class", "W+", "S_W+",
3110+
"Generalized W+", "Generalized S_W+"] for the cumulative weight types.
3111+
"""
3112+
from eis_toolkit.prediction.weights_of_evidence import weights_of_evidence_calculate_weights
3113+
3114+
typer.echo("Progress: 10%")
3115+
3116+
evidential_raster = rasterio.open(input_raster)
3117+
deposits = gpd.read_file(input_vector)
3118+
typer.echo("Progress: 25%")
3119+
3120+
if arrays_to_generate == []:
3121+
arrays_to_generate = None
3122+
3123+
df, arrays, raster_meta, nr_of_deposits, nr_of_pixels = weights_of_evidence_calculate_weights(
3124+
evidential_raster=evidential_raster,
3125+
deposits=deposits,
3126+
raster_nodata=raster_nodata,
3127+
weights_type=weights_type,
3128+
studentized_contrast_threshold=studentized_contrast_threshold,
3129+
arrays_to_generate=arrays_to_generate,
3130+
)
3131+
typer.echo("Progress: 75%")
3132+
3133+
df.to_csv(output_dir.joinpath("wofe_results.csv"))
3134+
3135+
file_name = input_raster.name.split(".")[0]
3136+
for key, array in arrays.items():
3137+
output_raster_path = output_dir.joinpath(file_name + "_weights_" + weights_type + "_" + key + ".tif")
3138+
with rasterio.open(output_raster_path, "w", **raster_meta) as dst:
3139+
dst.write(array, 1)
3140+
3141+
typer.echo("Progress 100%")
3142+
3143+
typer.echo(f"Number of deposit pixels: {nr_of_deposits}")
3144+
typer.echo(f"Number of all evidence pixels: {nr_of_pixels}")
3145+
typer.echo(f"Weight calculations completed, rasters and CSV saved to {output_dir}.")
3146+
3147+
3148+
@app.command()
3149+
def weights_of_evidence_calculate_responses_cli(
3150+
input_rasters_weights: INPUT_FILES_OPTION,
3151+
input_rasters_standard_deviations: INPUT_FILES_OPTION,
3152+
output_probabilities: OUTPUT_FILE_OPTION,
3153+
output_probabilities_std: OUTPUT_FILE_OPTION,
3154+
output_confidence_array: OUTPUT_FILE_OPTION,
3155+
nr_of_deposits: Annotated[int, typer.Option()],
3156+
nr_of_pixels: Annotated[int, typer.Option()],
3157+
):
3158+
"""
3159+
Calculate the posterior probabilities for the given generalized weight arrays.
3160+
3161+
Parameter --input-rasters are the output arrays (rasters) of weights-of-evidence-calculate-weights-cli.
3162+
For each set of rasters, generalized weight and generalized standard deviation arrays are used and summed
3163+
together pixel-wise to calculate the posterior probabilities. If generalized arrays are not found,
3164+
the W+ and S_W+ arrays are used (so if outputs from unique weight calculations are used for this function).
3165+
"""
3166+
from eis_toolkit.prediction.weights_of_evidence import weights_of_evidence_calculate_responses
3167+
3168+
typer.echo("Progress: 10%")
3169+
typer.echo(input_rasters_weights)
3170+
3171+
dict_array = []
3172+
raster_profile = None
3173+
3174+
for raster_weights, raster_std in zip_longest(
3175+
input_rasters_weights, input_rasters_standard_deviations, fillvalue=None
3176+
):
3177+
3178+
if raster_weights is not None:
3179+
with rasterio.open(raster_weights) as src:
3180+
array_W = src.read(1)
3181+
3182+
if raster_profile is None:
3183+
raster_profile = src.profile
3184+
3185+
if raster_std is not None:
3186+
with rasterio.open(raster_std) as src:
3187+
array_S_W = src.read(1)
3188+
3189+
dict_array.append({"W+": array_W, "S_W+": array_S_W})
3190+
3191+
typer.echo("Progress: 25%")
3192+
3193+
posterior_probabilities, posterior_probabilies_std, confidence_array = weights_of_evidence_calculate_responses(
3194+
output_arrays=dict_array, nr_of_deposits=nr_of_deposits, nr_of_pixels=nr_of_pixels
3195+
)
3196+
typer.echo("Progress: 75%")
3197+
3198+
with rasterio.open(output_probabilities, "w", **raster_profile) as dst:
3199+
dst.write(posterior_probabilities, 1)
3200+
3201+
with rasterio.open(output_probabilities_std, "w", **raster_profile) as dst:
3202+
dst.write(posterior_probabilies_std, 1)
3203+
3204+
with rasterio.open(output_confidence_array, "w", **raster_profile) as dst:
3205+
dst.write(confidence_array, 1)
3206+
3207+
typer.echo("Progress: 100%")
3208+
3209+
typer.echo(
3210+
f"Responses calculations finished, writing output rasters to {output_probabilities}, \
3211+
{output_probabilities_std} and {output_confidence_array}"
3212+
)
3213+
3214+
3215+
@app.command()
3216+
def agterberg_cheng_CI_test_cli(
3217+
input_posterior_probabilities: INPUT_FILE_OPTION,
3218+
input_posterior_probabilities_std: INPUT_FILE_OPTION,
3219+
nr_of_deposits: Annotated[int, typer.Option()],
3220+
):
3221+
"""Perform the conditional independence test presented by Agterberg-Cheng (2002)."""
3222+
from eis_toolkit.prediction.weights_of_evidence import agterberg_cheng_CI_test
3223+
3224+
typer.echo("Progress: 10%")
3225+
3226+
with rasterio.open(input_posterior_probabilities) as src:
3227+
posterior_probabilities = src.read(1)
3228+
3229+
with rasterio.open(input_posterior_probabilities_std) as src:
3230+
posterior_probabilities_std = src.read(1)
3231+
3232+
typer.echo("Progress: 25%")
3233+
3234+
_, _, _, _, summary = agterberg_cheng_CI_test(
3235+
posterior_probabilities=posterior_probabilities,
3236+
posterior_probabilities_std=posterior_probabilities_std,
3237+
nr_of_deposits=nr_of_deposits,
3238+
)
3239+
3240+
typer.echo("Progress: 100%")
3241+
typer.echo("Conditional independence test completed.")
3242+
typer.echo(summary)
30693243

30703244

30713245
# --- TRANSFORMATIONS ---

eis_toolkit/prediction/weights_of_evidence.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,11 @@ def weights_of_evidence_calculate_weights(
374374
that class with max contrast has studentized contrast value at least the defined value (cumulative).
375375
Defaults to 1.
376376
arrays_to_generate: Arrays to generate from the computed weight metrics. All column names
377-
in the produced weights_df are valid choices. Defaults to ["Class", "W+", "S_W+]
378-
for "unique" weights_type and ["Class", "W+", "S_W+", "Generalized W+", "Generalized S_W+"]
379-
for the cumulative weight types.
377+
in the produced weights_df are valid choices. Available column names for "unique" weights type are "Class",
378+
"Pixel count", "Deposit count", "W+", "S_W+", "W-", "S_W-", "Contrast", "S_Contrast", and
379+
"Studentized contrast". For other weights types, additional available column names are "Generalized class",
380+
"Generalzed W+", and "Generalized S_W+". Defaults to ["Class", "W+", "S_W+] for "unique" weights_type and
381+
["Class", "W+", "S_W+", "Generalized W+", "Generalized S_W+"] for the cumulative weight types.
380382
381383
Returns:
382384
Dataframe with weights of spatial association between the input data.

0 commit comments

Comments
 (0)