-
Notifications
You must be signed in to change notification settings - Fork 609
feat(tdigest): implement TDIGEST.TRIMMED_MEAN command #3312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: unstable
Are you sure you want to change the base?
Changes from all commits
6a2198f
7799524
4394482
178b1ee
8b2bb67
cf5451a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -309,3 +309,65 @@ inline Status TDigestRank(TD&& td, const std::vector<double>& inputs, std::vecto | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Status::OK(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| template <typename TD> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| inline StatusOr<double> TDigestTrimmedMean(TD&& td, double low_cut_quantile, double high_cut_quantile) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (td.Size() == 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Status{Status::InvalidArgument, "empty tdigest"}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (low_cut_quantile < 0.0 || low_cut_quantile > 1.0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Status{Status::InvalidArgument, "low cut quantile must be between 0 and 1"}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (high_cut_quantile < 0.0 || high_cut_quantile > 1.0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Status{Status::InvalidArgument, "high cut quantile must be between 0 and 1"}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (low_cut_quantile >= high_cut_quantile) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Status{Status::InvalidArgument, "low cut quantile must be less than high cut quantile"}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+319
to
+327
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move to the command parse step. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double low_boundary = std::numeric_limits<double>::quiet_NaN(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double high_boundary = std::numeric_limits<double>::quiet_NaN(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (low_cut_quantile == 0.0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use a more stable way of comparing doubles. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| low_boundary = td.Min(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auto low_result = TDigestQuantile(td, low_cut_quantile); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!low_result) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return low_result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| low_boundary = *low_result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (high_cut_quantile == 1.0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| high_boundary = td.Max(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auto high_result = TDigestQuantile(td, high_cut_quantile); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Iterate through the whole centroids to get centroids within the boundaries.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Plus, you have iterated the centroids twice after get the quantile. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!high_result) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return high_result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| high_boundary = *high_result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auto iter = td.Begin(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double total_weight_in_range = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double weighted_sum = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (iter->Valid()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auto centroid = GET_OR_RET(iter->GetCentroid()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ((low_cut_quantile == 0.0 && high_cut_quantile == 1.0) || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (centroid.mean >= low_boundary && centroid.mean <= high_boundary)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| total_weight_in_range += centroid.weight; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| weighted_sum += centroid.mean * centroid.weight; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| iter->Next(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (total_weight_in_range == 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+329
to
+368
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double low_boundary = std::numeric_limits<double>::quiet_NaN(); | |
| double high_boundary = std::numeric_limits<double>::quiet_NaN(); | |
| if (low_cut_quantile == 0.0) { | |
| low_boundary = td.Min(); | |
| } else { | |
| auto low_result = TDigestQuantile(td, low_cut_quantile); | |
| if (!low_result) { | |
| return low_result; | |
| } | |
| low_boundary = *low_result; | |
| } | |
| if (high_cut_quantile == 1.0) { | |
| high_boundary = td.Max(); | |
| } else { | |
| auto high_result = TDigestQuantile(td, high_cut_quantile); | |
| if (!high_result) { | |
| return high_result; | |
| } | |
| high_boundary = *high_result; | |
| } | |
| auto iter = td.Begin(); | |
| double total_weight_in_range = 0; | |
| double weighted_sum = 0; | |
| while (iter->Valid()) { | |
| auto centroid = GET_OR_RET(iter->GetCentroid()); | |
| if ((low_cut_quantile == 0.0 && high_cut_quantile == 1.0) || | |
| (centroid.mean >= low_boundary && centroid.mean <= high_boundary)) { | |
| total_weight_in_range += centroid.weight; | |
| weighted_sum += centroid.mean * centroid.weight; | |
| } | |
| iter->Next(); | |
| } | |
| if (total_weight_in_range == 0) { | |
| // First, compute the total weight of the t-digest. | |
| double total_weight = 0.0; | |
| { | |
| auto iter = td.Begin(); | |
| while (iter->Valid()) { | |
| auto centroid = GET_OR_RET(iter->GetCentroid()); | |
| total_weight += centroid.weight; | |
| iter->Next(); | |
| } | |
| } | |
| if (total_weight == 0.0) { | |
| return std::numeric_limits<double>::quiet_NaN(); | |
| } | |
| // If no trimming is requested, just return the global weighted mean. | |
| if (low_cut_quantile == 0.0 && high_cut_quantile == 1.0) { | |
| double weighted_sum = 0.0; | |
| auto iter = td.Begin(); | |
| while (iter->Valid()) { | |
| auto centroid = GET_OR_RET(iter->GetCentroid()); | |
| weighted_sum += centroid.mean * centroid.weight; | |
| iter->Next(); | |
| } | |
| return weighted_sum / total_weight; | |
| } | |
| // Compute rank boundaries in weight space. | |
| const double low_rank = low_cut_quantile * total_weight; | |
| const double high_rank = high_cut_quantile * total_weight; | |
| double cumulative_weight = 0.0; | |
| double total_weight_in_range = 0.0; | |
| double weighted_sum = 0.0; | |
| auto iter = td.Begin(); | |
| while (iter->Valid()) { | |
| auto centroid = GET_OR_RET(iter->GetCentroid()); | |
| const double start_rank = cumulative_weight; | |
| const double end_rank = cumulative_weight + centroid.weight; | |
| // If this centroid is entirely before the trimmed region, skip it. | |
| if (end_rank <= low_rank) { | |
| cumulative_weight = end_rank; | |
| iter->Next(); | |
| continue; | |
| } | |
| // If we've passed the trimmed region, we can stop. | |
| if (start_rank >= high_rank) { | |
| break; | |
| } | |
| // Compute overlap of this centroid's weight with [low_rank, high_rank). | |
| double overlap_start = start_rank; | |
| if (overlap_start < low_rank) { | |
| overlap_start = low_rank; | |
| } | |
| double overlap_end = end_rank; | |
| if (overlap_end > high_rank) { | |
| overlap_end = high_rank; | |
| } | |
| const double overlap = overlap_end - overlap_start; | |
| if (overlap > 0.0) { | |
| total_weight_in_range += overlap; | |
| weighted_sum += centroid.mean * overlap; | |
| } | |
| cumulative_weight = end_rank; | |
| iter->Next(); | |
| } | |
| if (total_weight_in_range == 0.0) { |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -524,3 +524,32 @@ TEST_F(RedisTDigestTest, ByRank_And_ByRevRank) { | |
| EXPECT_EQ(result[0], 1.0) << "Rank 0 should be minimum"; | ||
| EXPECT_TRUE(std::isinf(result[3])) << "Rank >= total_weight should be infinity"; | ||
| } | ||
|
|
||
| TEST_F(RedisTDigestTest, TrimmedMean) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add cases for invalid arguments and more unordered and complex inputs. |
||
| std::string test_digest_name = "test_digest_trimmed_mean" + std::to_string(util::GetTimeStampMS()); | ||
| bool exists = false; | ||
| auto status = tdigest_->Create(*ctx_, test_digest_name, {100}, &exists); | ||
| ASSERT_FALSE(exists); | ||
| ASSERT_TRUE(status.ok()); | ||
|
|
||
| std::vector<double> values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; | ||
| status = tdigest_->Add(*ctx_, test_digest_name, values); | ||
| ASSERT_TRUE(status.ok()) << status.ToString(); | ||
|
|
||
| redis::TDigestTrimmedMeanResult result; | ||
| status = tdigest_->TrimmedMean(*ctx_, test_digest_name, 0.1, 0.9, &result); | ||
| ASSERT_TRUE(status.ok()) << status.ToString(); | ||
| ASSERT_TRUE(result.mean.has_value()); | ||
| EXPECT_NEAR(*result.mean, 5.5, 1.0) << "Trimmed mean should be approximately 5.5"; | ||
|
|
||
| status = tdigest_->TrimmedMean(*ctx_, test_digest_name, 0.0, 1.0, &result); | ||
| ASSERT_TRUE(status.ok()) << status.ToString(); | ||
| ASSERT_TRUE(result.mean.has_value()); | ||
| EXPECT_NEAR(*result.mean, 5.5, 0.1) << "Full range should equal complete mean"; | ||
|
|
||
| status = tdigest_->TrimmedMean(*ctx_, test_digest_name, 0.25, 0.75, &result); | ||
| ASSERT_TRUE(status.ok()) << status.ToString(); | ||
| ASSERT_TRUE(result.mean.has_value()); | ||
| EXPECT_GT(*result.mean, 3.0) << "Trimmed mean should be greater than 3.0"; | ||
| EXPECT_LT(*result.mean, 8.0) << "Trimmed mean should be less than 8.0"; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -717,6 +717,101 @@ func tdigestTests(t *testing.T, configs util.KvrocksServerConfigs) { | |||||||||||||||||||||||
| require.EqualValues(t, expected[i], rank, "REVRANK mismatch at index %d", i) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| t.Run("TDIGEST.TRIMMED_MEAN with non-existent key", func(t *testing.T) { | ||||||||||||||||||||||||
| require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", "nonexistent", "0.1", "0.9").Err(), errMsgKeyNotExist) | ||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| t.Run("TDIGEST.TRIMMED_MEAN with empty tdigest", func(t *testing.T) { | ||||||||||||||||||||||||
| emptyKey := "tdigest_empty" | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", emptyKey, "compression", "100").Err()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| result := rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", emptyKey, "0.1", "0.9") | ||||||||||||||||||||||||
| require.NoError(t, result.Err()) | ||||||||||||||||||||||||
| require.Equal(t, "nan", result.Val()) | ||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| t.Run("TDIGEST.TRIMMED_MEAN with basic data set", func(t *testing.T) { | ||||||||||||||||||||||||
| key := "tdigest_basic" | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, "compression", "100").Err()) | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10").Err()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| result := rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", key, "0.1", "0.9") | ||||||||||||||||||||||||
| require.NoError(t, result.Err()) | ||||||||||||||||||||||||
| mean, err := strconv.ParseFloat(result.Val().(string), 64) | ||||||||||||||||||||||||
| require.NoError(t, err) | ||||||||||||||||||||||||
| require.InDelta(t, 5.5, mean, 1.0) | ||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the delta |
||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| t.Run("TDIGEST.TRIMMED_MEAN with no trimming", func(t *testing.T) { | ||||||||||||||||||||||||
| key := "tdigest_no_trim" | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, "compression", "100").Err()) | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10").Err()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| result := rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", key, "0", "1") | ||||||||||||||||||||||||
| require.NoError(t, result.Err()) | ||||||||||||||||||||||||
| mean, err := strconv.ParseFloat(result.Val().(string), 64) | ||||||||||||||||||||||||
| require.NoError(t, err) | ||||||||||||||||||||||||
| require.InDelta(t, 5.5, mean, 0.1) | ||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| t.Run("TDIGEST.TRIMMED_MEAN with skewed data", func(t *testing.T) { | ||||||||||||||||||||||||
| key := "tdigest_skewed" | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, "compression", "100").Err()) | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "1", "1", "1", "1", "10", "100").Err()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| result := rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", key, "0.2", "0.8") | ||||||||||||||||||||||||
| require.NoError(t, result.Err()) | ||||||||||||||||||||||||
| mean, err := strconv.ParseFloat(result.Val().(string), 64) | ||||||||||||||||||||||||
| require.NoError(t, err) | ||||||||||||||||||||||||
| require.Less(t, mean, 50.0) | ||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why we use |
||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| t.Run("TDIGEST.TRIMMED_MEAN wrong number of arguments", func(t *testing.T) { | ||||||||||||||||||||||||
| require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN").Err(), errMsgWrongNumberArg) | ||||||||||||||||||||||||
| require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", "key").Err(), errMsgWrongNumberArg) | ||||||||||||||||||||||||
| require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", "key", "0.1").Err(), errMsgWrongNumberArg) | ||||||||||||||||||||||||
| require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", "key", "0.1", "0.9", "extra").Err(), errMsgWrongNumberArg) | ||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| t.Run("TDIGEST.TRIMMED_MEAN invalid quantile ranges", func(t *testing.T) { | ||||||||||||||||||||||||
| key := "tdigest_invalid" | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, "compression", "100").Err()) | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", "3", "4", "5").Err()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", key, "-0.1", "0.9").Err(), "low cut quantile must be between 0 and 1") | ||||||||||||||||||||||||
| require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", key, "0.1", "1.1").Err(), "high cut quantile must be between 0 and 1") | ||||||||||||||||||||||||
| require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", key, "0.9", "0.1").Err(), "low cut quantile must be less than high cut quantile") | ||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error message could be constant string to reduce duplication. |
||||||||||||||||||||||||
| require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", key, "0.5", "0.5").Err(), "low cut quantile must be less than high cut quantile") | ||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| t.Run("TDIGEST.TRIMMED_MEAN with single value", func(t *testing.T) { | ||||||||||||||||||||||||
| key := "tdigest_single" | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, "compression", "100").Err()) | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "42").Err()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| result := rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", key, "0.1", "0.9") | ||||||||||||||||||||||||
| require.NoError(t, result.Err()) | ||||||||||||||||||||||||
| mean, err := strconv.ParseFloat(result.Val().(string), 64) | ||||||||||||||||||||||||
| require.NoError(t, err) | ||||||||||||||||||||||||
| require.InDelta(t, 42.0, mean, 0.001) | ||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we could use a stable precision for delta in all cases? |
||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| t.Run("TDIGEST.TRIMMED_MEAN with extreme trimming", func(t *testing.T) { | ||||||||||||||||||||||||
| key := "tdigest_extreme" | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, "compression", "100").Err()) | ||||||||||||||||||||||||
| require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10").Err()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| result := rdb.Do(ctx, "TDIGEST.TRIMMED_MEAN", key, "0.4", "0.6") | ||||||||||||||||||||||||
| require.NoError(t, result.Err()) | ||||||||||||||||||||||||
| meanStr := result.Val().(string) | ||||||||||||||||||||||||
| if meanStr == "nan" { | ||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The result should not be |
||||||||||||||||||||||||
| return | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| mean, err := strconv.ParseFloat(meanStr, 64) | ||||||||||||||||||||||||
| require.NoError(t, err) | ||||||||||||||||||||||||
| require.Greater(t, mean, 0.0) | ||||||||||||||||||||||||
|
Comment on lines
+808
to
+813
|
||||||||||||||||||||||||
| if meanStr == "nan" { | |
| return | |
| } | |
| mean, err := strconv.ParseFloat(meanStr, 64) | |
| require.NoError(t, err) | |
| require.Greater(t, mean, 0.0) | |
| mean, err := strconv.ParseFloat(meanStr, 64) | |
| require.NoError(t, err) | |
| require.False(t, math.IsNaN(mean)) | |
| require.Greater(t, mean, 4.0) | |
| require.Less(t, mean, 7.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use precise value for test cases for stable and correction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check the validation of
high_cut_quantileandlow_cut_quantile.The parameter validation should be done in the earliest step rather than in the command processing.