PGomes92 / pyhrv

Python toolbox for Heart Rate Variability
BSD 3-Clause "New" or "Revised" License
271 stars 77 forks source link

SD1/SD2 calculation not based on distances #44

Open weberk opened 9 months ago

weberk commented 9 months ago

Hi Pedro,

just wanted to verify my own Monkey C implementation of Pointcareé Plot with your data and results. Found major differences, as seen in the following screenshots legends (sd1 and sd2): image

in nonlinear.py there is the poincareé plot with the additional ellipse drawn from SD1 and SD2 standard deviation of the points distance to the two axes.

# SD1 & SD2 Computation
    sd1 = np.std(np.subtract(x1, x2) / np.sqrt(2))
    sd2 = np.std(np.add(x1, x2) / np.sqrt(2))

Both arrays are having issues and therefore both standard deviations are based on wrong values. sd1: when x1-x2 is calculated there can be negative numbers which are not to be treated as distance to the 45 degree axe sd2: adding x1+x2 is not at all giving the distance to the -45 degree axe since ignoring the position of the centroid point

Correct implementation as follows:

# Load NNI sample series
nni = pyhrv.utils.load_sample_nni()
nn = pyhrv.utils.check_input(nni)

# Prepare Poincaré data
x1 = np.asarray(nn[:-1])
x2 = np.asarray(nn[1:])

# SD1 & SD2 Computation
# sd1 = np.std(np.subtract(x1, x2) / np.sqrt(2))
sd1 = np.std(np.abs(np.subtract(x1, x2)) / np.sqrt(2))

# Initialization of an empty vector for sd2
sd2_values = []
mean_val = np.mean(nn)
x_center = mean_val
y_center = mean_val

# Loop through all elements of x2
for i in range(len(x2)):
    x_beatSample = x1[i]
    y_beatSample = x2[i]
    x_beatMirrorSample = x_center - (y_beatSample - y_center)
    y_beatMirrorSample = y_center - (x_beatSample - x_center)
    x_diff = x_beatMirrorSample - x_beatSample
    y_diff = y_beatMirrorSample - y_beatSample
    distancePoint_i = 0.5 * np.sqrt(x_diff**2 + y_diff**2)
    # Add the sd2 value to the list
    sd2_values.append(distancePoint_i)

# Output results
print("nn", nn)
print("distances used for SD1:", np.abs(np.subtract(x1, x2)) / np.sqrt(2))
print("distances used for SD2:", sd2_values)

sd2 = np.std(sd2_values)

print("SD1:", sd1);
print("SD2:", sd2);

Hope you or someone else can comment if my suggested implementation is correct?

Regards - Klaus.

P.s.: My Garmin Watch app is online since last december: --> https://apps.garmin.com/en-US/apps/1c02ac65-70c5-440e-a6e6-f4a706c8deb0