diff --git a/tobac/feature_detection.py b/tobac/feature_detection.py index 0bb9d810..79af1200 100644 --- a/tobac/feature_detection.py +++ b/tobac/feature_detection.py @@ -677,7 +677,7 @@ def feature_detection_multithreshold( # Loop over DataFrame to remove features that are closer than distance_min to each other: if min_distance > 0: features_thresholds = filter_min_distance( - features_thresholds, dxy, min_distance + features_thresholds, dxy, min_distance, target=target ) list_features_timesteps.append(features_thresholds) @@ -701,7 +701,7 @@ def feature_detection_multithreshold( return features -def filter_min_distance(features, dxy, min_distance): +def filter_min_distance(features, dxy, min_distance, target="maximum"): """Perform feature detection based on contiguous regions. Regions are above/below a threshold. @@ -716,6 +716,10 @@ def filter_min_distance(features, dxy, min_distance): min_distance : float, optional Minimum distance (in meter) between detected features. + target : str {maximum | minimum}, optional + Whether the threshod target is a maxima or minima (defaults to + maximum) + Returns ------- features : pandas.DataFrame @@ -724,6 +728,11 @@ def filter_min_distance(features, dxy, min_distance): from itertools import combinations + if target not in ["minimum", "maximum"]: + raise ValueError( + "target parameter must be set to either 'minimum' or 'maximum'" + ) + remove_list_distance = [] # create list of tuples with all combinations of features at the timestep: indices = combinations(features.index.values, 2) @@ -737,18 +746,8 @@ def filter_min_distance(features, dxy, min_distance): ** 2 ) if distance <= min_distance: - # logging.debug('distance<= min_distance: ' + str(distance)) + # If same threshold value, remove based on number of pixels if ( - features.loc[index_1, "threshold_value"] - > features.loc[index_2, "threshold_value"] - ): - remove_list_distance.append(index_2) - elif ( - features.loc[index_1, "threshold_value"] - < features.loc[index_2, "threshold_value"] - ): - remove_list_distance.append(index_1) - elif ( features.loc[index_1, "threshold_value"] == features.loc[index_2, "threshold_value"] ): @@ -756,7 +755,33 @@ def filter_min_distance(features, dxy, min_distance): remove_list_distance.append(index_2) elif features.loc[index_1, "num"] < features.loc[index_2, "num"]: remove_list_distance.append(index_1) + # Tie break if both have the same number of pixels elif features.loc[index_1, "num"] == features.loc[index_2, "num"]: remove_list_distance.append(index_2) + # Else remove based on comparison of thresholds and target + elif target == "maximum": + if ( + features.loc[index_1, "threshold_value"] + > features.loc[index_2, "threshold_value"] + ): + remove_list_distance.append(index_2) + elif ( + features.loc[index_1, "threshold_value"] + < features.loc[index_2, "threshold_value"] + ): + remove_list_distance.append(index_1) + + elif target == "minimum": + if ( + features.loc[index_1, "threshold_value"] + < features.loc[index_2, "threshold_value"] + ): + remove_list_distance.append(index_2) + elif ( + features.loc[index_1, "threshold_value"] + > features.loc[index_2, "threshold_value"] + ): + remove_list_distance.append(index_1) + features = features[~features.index.isin(remove_list_distance)] return features diff --git a/tobac/tests/test_feature_detection.py b/tobac/tests/test_feature_detection.py index 91fc335f..9826c82e 100644 --- a/tobac/tests/test_feature_detection.py +++ b/tobac/tests/test_feature_detection.py @@ -83,8 +83,8 @@ def test_filter_min_distance(test_threshs, min_distance, dxy): ## add another blob with smaller value test_hdim_1_pt2 = 25.0 test_hdim_2_pt2 = 25.0 - test_hdim_1_sz2 = 2 - test_hdim_2_sz2 = 2 + test_hdim_1_sz2 = 3 + test_hdim_2_sz2 = 3 test_amp2 = 3 test_data = tbtest.make_feature_blob( test_data, @@ -107,7 +107,9 @@ def test_filter_min_distance(test_threshs, min_distance, dxy): ) # check if it function to filter - fd_filtered = feat_detect.filter_min_distance(fd_output, dxy, min_distance) + fd_filtered = feat_detect.filter_min_distance( + fd_output, dxy, min_distance, target="maximum" + ) # Make sure we have only one feature (small feature in minimum distance should be removed ) assert len(fd_output.index) == 2 @@ -116,6 +118,18 @@ def test_filter_min_distance(test_threshs, min_distance, dxy): assert fd_filtered.iloc[0]["hdim_1"] == pytest.approx(test_hdim_1_pt) assert fd_filtered.iloc[0]["hdim_2"] == pytest.approx(test_hdim_2_pt) + # check if it function to filter + fd_filtered = feat_detect.filter_min_distance( + fd_output, dxy, min_distance, target="minimum" + ) + + # Make sure we have only one feature (small feature in minimum distance should be removed ) + assert len(fd_output.index) == 2 + assert len(fd_filtered.index) == 1 + # Make sure that the locations of the features is correct (should correspond to locations of second feature) + assert fd_filtered.iloc[0]["hdim_1"] == pytest.approx(test_hdim_1_pt2) + assert fd_filtered.iloc[0]["hdim_2"] == pytest.approx(test_hdim_2_pt2) + @pytest.mark.parametrize( "position_threshold", [("center"), ("extreme"), ("weighted_diff"), ("weighted_abs")] diff --git a/tobac/utils.py b/tobac/utils.py index bedcde1c..23bdf382 100644 --- a/tobac/utils.py +++ b/tobac/utils.py @@ -557,11 +557,15 @@ def add_coordinates(t, variable_cube): if variable_cube.coord_dims(coord) == (hdim_1, hdim_2): f = interp2d(dimvec_2, dimvec_1, variable_cube.coord(coord).points) - coordinate_points = [f(a, b) for a, b in zip(t["hdim_2"], t["hdim_1"])] + coordinate_points = np.asarray( + [f(a, b) for a, b in zip(t["hdim_2"], t["hdim_1"])] + ) if variable_cube.coord_dims(coord) == (hdim_2, hdim_1): f = interp2d(dimvec_1, dimvec_2, variable_cube.coord(coord).points) - coordinate_points = [f(a, b) for a, b in zip(t["hdim_1"], t["hdim_2"])] + coordinate_points = np.asarray( + [f(a, b) for a, b in zip(t["hdim_1"], t["hdim_2"])] + ) # interpolate 3D coordinates: # mainly workaround for wrf latitude and longitude (to be fixed in future)