Skip to content

Commit 960b53a

Browse files
committed
rename curvature enums to friendlier names
1 parent dc9870d commit 960b53a

File tree

2 files changed

+45
-27
lines changed

2 files changed

+45
-27
lines changed

Kneedle.Tests/Tests.cs

+11-11
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@ public void Setup()
2121
[Test]
2222
public void TestInvalidInputs()
2323
{
24-
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(null, null, Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Negative);
24+
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(null, null, Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Clockwise);
2525
Assert.IsNull(k);
26-
k = Kneedle.KneedleAlgorithm.CalculateKneePoints(new double[0], new double[1], Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Negative);
26+
k = Kneedle.KneedleAlgorithm.CalculateKneePoints(new double[0], new double[1], Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Clockwise);
2727
Assert.IsNull(k);
28-
k = Kneedle.KneedleAlgorithm.CalculateKneePoints(new double[1], new double[1], Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Negative);
28+
k = Kneedle.KneedleAlgorithm.CalculateKneePoints(new double[1], new double[1], Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Clockwise);
2929
Assert.IsNull(k);
3030
}
3131

3232
[Test]
3333
public void TestCurveWithoutKnee()
3434
{
35-
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(new double[2], new double[2], Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Negative);
35+
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(new double[2], new double[2], Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Clockwise);
3636
Assert.IsNull(k);
3737
}
3838

@@ -41,7 +41,7 @@ public void TestShortCurve()
4141
{
4242
var y = new double[] { 2, 4, 10 };
4343
var x = Enumerable.Range(0, y.Length).Select(v => (double)v).ToArray();
44-
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x, y, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Positive);
44+
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x, y, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Counterclockwise);
4545
Assert.IsNotNull(k);
4646
Assert.AreEqual(1d, k.Value);
4747
}
@@ -51,15 +51,15 @@ public void TestSparseCurve()
5151
{
5252
var y = new double[] { 2, 4, 10 };
5353
var x = new double[] { 0, 7, 100 };
54-
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x, y, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Positive);
54+
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x, y, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Counterclockwise);
5555
Assert.IsNotNull(k);
5656
Assert.AreEqual(7d, k.Value);
5757
}
5858

5959
[Test]
6060
public void TestIncreasingPositiveCurvature()
6161
{
62-
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x_all, y_pos_inc, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Positive);
62+
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x_all, y_pos_inc, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Counterclockwise);
6363
Assert.IsNotNull(k);
6464
Console.WriteLine(k.Value);
6565
Assert.AreEqual(k.Value, 67d);
@@ -68,7 +68,7 @@ public void TestIncreasingPositiveCurvature()
6868
[Test]
6969
public void TestDecreasingPositiveCurvature()
7070
{
71-
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x_all, y_pos_dec, Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Positive);
71+
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x_all, y_pos_dec, Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Counterclockwise);
7272
Assert.IsNotNull(k);
7373
Console.WriteLine(k.Value);
7474
Assert.AreEqual(k.Value, 32d);
@@ -77,7 +77,7 @@ public void TestDecreasingPositiveCurvature()
7777
[Test]
7878
public void TestIncreasingNegativeCurvature()
7979
{
80-
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x_all, y_neg_inc, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Negative);
80+
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x_all, y_neg_inc, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Clockwise);
8181
Assert.IsNotNull(k);
8282
Console.WriteLine(k.Value);
8383
Assert.AreEqual(k.Value, 32d);
@@ -86,7 +86,7 @@ public void TestIncreasingNegativeCurvature()
8686
[Test]
8787
public void TestDecreasingNegativeCurvature()
8888
{
89-
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x_all, y_neg_dec, Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Negative);
89+
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x_all, y_neg_dec, Kneedle.CurveDirection.Decreasing, Kneedle.Curvature.Clockwise);
9090
Assert.IsNotNull(k);
9191
Console.WriteLine(k.Value);
9292
Assert.AreEqual(k.Value, 67d);
@@ -101,7 +101,7 @@ public void TestGaussianNegativeCurvature()
101101
Array.Sort(x);
102102
var y = Enumerable.Range(0, numSamples).Select(v => 1.0 * v / numSamples).ToArray();
103103

104-
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x, y, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Negative);
104+
var k = Kneedle.KneedleAlgorithm.CalculateKneePoints(x, y, Kneedle.CurveDirection.Increasing, Kneedle.Curvature.Clockwise);
105105
Assert.IsNotNull(k);
106106
Console.WriteLine(k.Value);
107107
Assert.IsTrue(Math.Abs(k.Value - 60.5) <= 5.0);

Kneedle/Kneedle.cs

+34-16
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
using MathNet.Numerics;
1+

2+
3+
using MathNet.Numerics;
24
using System;
35
using System.Collections.Generic;
46
using System.Linq;
5-
using System.Numerics;
67

78
namespace Kneedle
89
{
@@ -15,28 +16,45 @@ public enum CurveDirection
1516
public enum Curvature
1617
{
1718
/* tangent goes anti-clockwise */
18-
Positive,
19+
Counterclockwise,
1920
/* tangent goes clockwise */
20-
Negative
21+
Clockwise
2122
}
2223

2324
public static class KneedleAlgorithm
2425
{
2526
/**
27+
* <summary>
28+
* <para>
2629
* Calculates knee points using the Kneedle algorithm. Returns the x value corresponding to the knee
2730
* point when successful, null otherwise.
31+
* </para>
32+
* <para>
2833
* Reference:
2934
* Finding a ‘kneedle’in a haystack: Detecting knee points in system behavior.
3035
* Satopaa, V and Albrecht, J and Irwin, D and Raghavan, B
31-
* https://raghavan.usc.edu/papers/kneedle-simplex11.pdf
32-
*
33-
* Needs the data points provided x-sorted
34-
* Positive curvature is when the tangent traces anti-clockwise
36+
* <see cref="https://raghavan.usc.edu/papers/kneedle-simplex11.pdf"/>
37+
* </para>
38+
*
39+
* <list type="bullet">
40+
* <param name="x">x: X axis values of the points. Points must be sorted in ascending order w.r.t. X axis.</param>
41+
* <param name="y">y: Y axis values of the points.</param>
42+
* <param name="direction">direction: If the curve is increasing or decreasing. Make sure to set this value according to the input curve.</param>
43+
* <param name="concavity">concavity: Whether the curve has positive or negative curvature. In other words, concave or convex. Whether the tangent rotates clockwise or counterclockwise. Make sure to set this value according to the input curve.</param>
44+
* <param name="sensitivity">sensitivity: Adjusts the knee detection threshold. Defaults to 1 as per the paper.</param>
45+
* <param name="forceLinearInterpolation">forceLinearInterpolation: Interpolation is done using robust cubic splines. For some inputs, spline can overshoot. This param forces linear interpolation instead of cubic spline.</param>
46+
* </list>
47+
*
3548
* Can return null when the algorithm fails to identify a knee/elbow for various reasons:
3649
* - the number of data points is too small
3750
* - there are no local maxima on the diffs, which means either the curve is a line, or the
3851
* parameters provided are incompatible with the curve
39-
*
52+
*
53+
* <list type="bullet">
54+
* 2019-01-08: rename curvature enum to be easy to interpret and remember (Prashant Borole)
55+
* 2019-01-07: initial version (Prashant Borole)
56+
* </list>
57+
* </summary>
4058
*/
4159
public static double? CalculateKneePoints(double[] x, double[] y, CurveDirection direction, Curvature concavity, double sensitivity = 1, bool forceLinearInterpolation = true)
4260
{
@@ -62,13 +80,13 @@ public static class KneedleAlgorithm
6280

6381
var x_diff = x_norm;
6482
var y_diff = new double[numPoints];
65-
if(direction == CurveDirection.Decreasing)
83+
if (direction == CurveDirection.Decreasing)
6684
{
6785
for (int i = 0; i < numPoints; i++)
6886
{
6987
y_diff[i] = x_norm[i] + y_norm[i];
7088
}
71-
if(concavity == Curvature.Positive )
89+
if (concavity == Curvature.Counterclockwise)
7290
{
7391
for (int i = 0; i < numPoints; i++)
7492
{
@@ -83,7 +101,7 @@ public static class KneedleAlgorithm
83101
{
84102
y_diff[i] = y_norm[i] - x_norm[i];
85103
}
86-
if (concavity == Curvature.Positive)
104+
if (concavity == Curvature.Counterclockwise)
87105
{
88106
for (int i = 0; i < numPoints; i++)
89107
{
@@ -120,15 +138,15 @@ public static class KneedleAlgorithm
120138
continue;
121139
}
122140

123-
if(xmn_idxs_set.Contains(x_i))
141+
if (xmn_idxs_set.Contains(x_i))
124142
{
125-
if(x_i < x.Length - 1 && y_diff[x_i + 1] > y_diff[x_i])
143+
if (x_i < x.Length - 1 && y_diff[x_i + 1] > y_diff[x_i])
126144
{
127145
tmx[curMaximaIdx] = 0;
128146
}
129147
}
130148

131-
if(y_diff[x_i] < tmx[curMaximaIdx] || tmx[curMaximaIdx] < 0)
149+
if (y_diff[x_i] < tmx[curMaximaIdx] || tmx[curMaximaIdx] < 0)
132150
{
133151
knee = x[xmx_idxs[curMaximaIdx]];
134152
}
@@ -166,7 +184,7 @@ public static List<int> FindLocalExtrema(double[] arr, bool max)
166184
var lcomp = max ? cur > prev : cur < prev;
167185
var rcomp = max ? cur > next : cur < next;
168186

169-
if(lcomp && rcomp)
187+
if (lcomp && rcomp)
170188
{
171189
ret.Add(i);
172190
}

0 commit comments

Comments
 (0)