Optimal number of clusters - Error in FUNcluster(x, i, ...) : more cluster centers than distinct data points - numbers

I have these data and I need to find the optimal number of clusters of this table.
The values can be either 0, 0.5 or 1
library(NbClust)
library(factoextra)
library(pheatmap)
tab=structure(list(`57-B1` = c(1, 0.5, 0.5, 1, 1, 0.5), `57-B3` = c(0.5,
0.5, 0.5, 0, 0.5, 0.5), `57-C1` = c(1, 0.5, 0.5, 0.5, 1, 0.5),
`57-C5` = c(1, 0.5, 0.5, 1, 1, 1), `57-H2` = c(1, 0.5, 0.5,
0, 1, 1), `57-H4` = c(0.5, 0.5, 0.5, 0, 0.5, 0.5), `61-1-B1` = c(0.5,
0.5, 0.5, 0, 0.5, 0.5), `61-1-C2` = c(0.5, 0.5, 0.5, 0, 0.5,
0.5), `61-1-C5` = c(0.5, 0.5, 0.5, 0, 0.5, 0.5), `61-1-H1` = c(0.5,
0.5, 0, 0, 0.5, 0.5), `61-1-H3` = c(0.5, 0.5, 0.5, 0, 0.5,
0.5), `61-1-H5` = c(0.5, 0.5, 0, 0.5, 0.5, 0.5), `62-2_H2` = c(0.5,
0.5, 0.5, 0, 0.5, 0.5), `62_1_C2` = c(0.5, 0.5, 0, 0.5, 0.5,
0.5), `62_1_C5` = c(0.5, 0.5, 0.5, 0, 0.5, 0.5), `FL-39-C3` = c(0.5,
0.5, 0.5, 0, 0.5, 0.5), `FL-41-1-C3` = c(0.5, 0.5, 0.5, 0,
0.5, 0.5), `FL-57-B1` = c(0.5, 0.5, 0.5, 0, 0.5, 0.5), `FL-57-B2` = c(0.5,
0.5, 0.5, 0, 0.5, 0.5), `FL-57-C2` = c(0.5, 0.5, 0, 0.5,
0.5, 0.5), `FL-57-C3` = c(1, 1, 1, 0, 1, 1), `FL-57-C5` = c(1,
0.5, 0.5, 1, 1, 1), `FL-57-H1` = c(1, 0.5, 0.5, 1, 1, 1),
`FL-57-H4` = c(0.5, 0.5, 0, 0, 0.5, 0.5), `FL-57-H5` = c(0.5,
0.5, 0.5, 0, 0.5, 0.5), `FL-61-1-B1` = c(0.5, 0.5, 0.5, 0,
0.5, 0.5), `FL-61-1-B4` = c(0.5, 0.5, 0.5, 0, 0.5, 0.5),
`FL-61-1-C2` = c(0.5, 0.5, 0, 0, 0.5, 0.5), `FL-61-1-C4` = c(0.5,
0.5, 0.5, 0, 0.5, 0.5), `FL-61-1-H3` = c(0.5, 0.5, 0.5, 0,
0.5, 0.5), `FL-61-1-H4` = c(0.5, 0.5, 0.5, 0, 0.5, 0.5),
`FL-61-1-H5` = c(0.5, 0.5, 0, 0.5, 0.5, 0.5), `FL-62-1-C3` = c(0.5,
0.5, 0, 0.5, 0.5, 0.5), `FL-62-2-H2` = c(0.5, 0.5, 0.5, 0,
0.5, 0.5), `FL-73-H1` = c(0.5, 0.5, 0.5, 0, 0.5, 0.5), P_57_F = c(0.5,
0.5, 0.5, 0, 0.5, 0.5), P_57_M = c(0.5, 0.5, 0.5, 0, 0.5,
0.5)), row.names = c("g1", "g2", "g3", "g4", "g5", "g6"), class = "data.frame")
I tried both on scaled and non-scale values:
fviz_nbclust(scale(tab), kmeans, method = "wss")
fviz_nbclust(tab, kmeans, method = "wss")
and I get this error:
Error in FUNcluster(x, i, ...) :
more cluster centers than distinct data points.
how can I fix it?
Many thanks for your help !

I maybe found the solutions: it was sufficient to specify k.max = any number lower than nrow(tab)

Related

Circle stuck at top left hand corner of screen swiftUI

I'm trying to draw a circle in SwiftUI at a constantly changing variable poseEstimator.bodyparts[.leftshoulder]!.location of type CGPoint on my View, and the end goal is that when the variable changes, I want the circle on the screen to move to the new coordinates stored in the variable.
I read up online on how to draw a circle in swiftUI and found out that you need to use the Circle() class to draw a circle, so that's what I did, but for some reason, my circle is always stuck at top left hand corner of the screen(0.0, 0.0), and even when I change the .position property of the circle to CGPoint(0.5,0.5), the circle is still stuck at the exact same location as before.
This is my swiftUI view struct:
import SwiftUI
struct StickFigureView: View { //declare the stickfigure for pose esimation as a swift view
#ObservedObject var poseEstimator: PoseEstimator
var size: CGSize
var body: some View {
if poseEstimator.bodyParts.isEmpty == false { //add in the sticks only if there are VNPoints stored in "bodyparts"
ZStack {
// Right leg
if poseEstimator.bodyParts[.rightAnkle]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.rightKnee]!.location != CGPoint(x: 0.0, y: 1.0){
Stick(points: [poseEstimator.bodyParts[.rightAnkle]!.location,
poseEstimator.bodyParts[.rightKnee]!.location], size: size)
.stroke(lineWidth: 5.0)
.fill(Color.blue)
}
Circle()
.fill(Color.blue)
.frame(width: 10, height: 10)
.position(CGPoint(x: 0.5, y: 0.5)) // <- this is the circle which I have problems with
if poseEstimator.bodyParts[.rightKnee]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.rightHip]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.root]!.location != CGPoint(x: 0.0, y: 1.0) {
Stick(points: [poseEstimator.bodyParts[.rightKnee]!.location,
poseEstimator.bodyParts[.rightHip]!.location,
poseEstimator.bodyParts[.root]!.location], size: size)
.stroke(lineWidth: 5.0)
.fill(Color.blue)
}
// Left leg
if poseEstimator.bodyParts[.leftAnkle]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.leftKnee]!.location != CGPoint(x: 0.0, y: 1.0){
Stick(points: [poseEstimator.bodyParts[.leftAnkle]!.location,
poseEstimator.bodyParts[.leftKnee]!.location], size: size)
.stroke(lineWidth: 5.0)
.fill(Color.blue)
}
if poseEstimator.bodyParts[.leftKnee]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.leftHip]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.root]!.location != CGPoint(x: 0.0, y: 1.0){
Stick(points: [poseEstimator.bodyParts[.leftKnee]!.location,
poseEstimator.bodyParts[.leftHip]!.location,
poseEstimator.bodyParts[.root]!.location], size: size)
.stroke(lineWidth: 5.0)
.fill(Color.blue)
}
// Right arm
if poseEstimator.bodyParts[.rightWrist]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.rightElbow]!.location != CGPoint(x: 0.0, y: 1.0){
Stick(points: [poseEstimator.bodyParts[.rightWrist]!.location,
poseEstimator.bodyParts[.rightElbow]!.location], size: size)
.stroke(lineWidth: 5.0)
.fill(Color.blue)
}
if poseEstimator.bodyParts[.rightWrist]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.rightElbow]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.neck]!.location != CGPoint(x: 0.0, y: 1.0){
Stick(points: [poseEstimator.bodyParts[.rightElbow]!.location,
poseEstimator.bodyParts[.rightShoulder]!.location,
poseEstimator.bodyParts[.neck]!.location], size: size)
.stroke(lineWidth: 5.0)
.fill(Color.blue)
}
// Left arm
if poseEstimator.bodyParts[.leftWrist]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.leftElbow]!.location != CGPoint(x: 0.0, y: 1.0){
Stick(points: [poseEstimator.bodyParts[.leftWrist]!.location,
poseEstimator.bodyParts[.leftElbow]!.location], size: size)
.stroke(lineWidth: 5.0)
.fill(Color.blue)
}
if poseEstimator.bodyParts[.leftElbow]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.leftShoulder]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.neck]!.location != CGPoint(x: 0.0, y: 1.0){
dot(Position: CGPoint(x: 0.5, y: 0.5), Color: Color.blue)
.position(CGPoint(x: 0.5, y: 0.5))
}
// Root to nose
if poseEstimator.bodyParts[.root]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.neck]!.location != CGPoint(x: 0.0, y: 1.0){
Stick(points: [poseEstimator.bodyParts[.root]!.location,
poseEstimator.bodyParts[.neck]!.location], size: size)
.stroke(lineWidth: 5.0)
.fill(Color.blue)
}
}
}
}
}
Ok I've finally cracked the code. #jnpdx was right, the CGPoint that I set was too small to be even rendered in the first place. Upon further troubleshooting, I found out that the variable poseEstimator.bodyparts[.leftshoulder]!.location that I assigned to the position property of the circle was around 0.3, which was too small to be seen. I had to scale the variable using CGAffineTransform so that the CGpoint was relative to the width and height of the screen. This fixed the problem.
import SwiftUI
#available(iOS 15.0, *)
struct dotview: View { //declare the stickfigure for pose esimation as a swift view
#ObservedObject var poseEstimator: PoseEstimator
var size: CGSize
var body: some View {
if poseEstimator.bodyParts.isEmpty == false { //add in the sticks only if there are VNPoints stored in "bodyparts"
ZStack {
// Right leg
if poseEstimator.bodyParts[.rightAnkle]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.rightKnee]!.location != CGPoint(x: 0.0, y: 1.0){
Circle()
.fill(poseEstimator.rightanklecolor)
.position(poseEstimator.bodyParts[.rightAnkle]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
Circle()
.fill(poseEstimator.rightkneecolor)
.position(poseEstimator.bodyParts[.rightKnee]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
}
if poseEstimator.bodyParts[.rightKnee]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.rightHip]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.root]!.location != CGPoint(x: 0.0, y: 1.0) {
Circle()
.fill(poseEstimator.rightkneecolor)
.position(poseEstimator.bodyParts[.rightKnee]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
Circle()
.fill(poseEstimator.righthipcolor)
.position(poseEstimator.bodyParts[.rightHip]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
}
// Left leg
if poseEstimator.bodyParts[.leftAnkle]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.leftKnee]!.location != CGPoint(x: 0.0, y: 1.0){
Circle()
.fill(poseEstimator.leftanklecolor)
.position(poseEstimator.bodyParts[.leftAnkle]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
Circle()
.fill(poseEstimator.leftkneecolor)
.position(poseEstimator.bodyParts[.leftKnee]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
}
if poseEstimator.bodyParts[.leftKnee]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.leftHip]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.root]!.location != CGPoint(x: 0.0, y: 1.0){
Circle()
.fill(poseEstimator.leftkneecolor)
.position(poseEstimator.bodyParts[.leftKnee]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
Circle()
.fill(poseEstimator.lefthipcolor)
.position(poseEstimator.bodyParts[.leftHip]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
}
// Right arm
if poseEstimator.bodyParts[.rightWrist]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.rightElbow]!.location != CGPoint(x: 0.0, y: 1.0){
Circle()
.fill(poseEstimator.rightwristcolor)
.position(poseEstimator.bodyParts[.rightWrist]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
Circle()
.fill(poseEstimator.rightelbowcolor)
.position(poseEstimator.bodyParts[.rightElbow]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
}
if poseEstimator.bodyParts[.rightElbow]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.rightShoulder]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.neck]!.location != CGPoint(x: 0.0, y: 1.0){
Circle()
.fill(poseEstimator.rightelbowcolor)
.position(poseEstimator.bodyParts[.rightElbow]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
Circle()
.fill(poseEstimator.rightshouldercolor)
.position(poseEstimator.bodyParts[.rightShoulder]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
}
// Left arm
if poseEstimator.bodyParts[.leftWrist]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.leftElbow]!.location != CGPoint(x: 0.0, y: 1.0){
Circle()
.fill(poseEstimator.leftwristcolor)
.position(poseEstimator.bodyParts[.leftWrist]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
Circle()
.fill(poseEstimator.leftelbowcolor)
.position(poseEstimator.bodyParts[.leftElbow]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
}
if poseEstimator.bodyParts[.leftElbow]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.leftShoulder]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.neck]!.location != CGPoint(x: 0.0, y: 1.0){
Circle()
.fill(poseEstimator.leftelbowcolor)
.position(poseEstimator.bodyParts[.leftElbow]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
Circle()
.fill(poseEstimator.leftshouldercolor)
.position(poseEstimator.bodyParts[.leftShoulder]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
}
// Root to nose
if poseEstimator.bodyParts[.root]!.location != CGPoint(x: 0.0, y: 1.0) && poseEstimator.bodyParts[.neck]!.location != CGPoint(x: 0.0, y: 1.0){
Circle()
.fill(poseEstimator.rootcolor)
.position(poseEstimator.bodyParts[.root]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
Circle()
.fill(poseEstimator.neckcolor)
.position(poseEstimator.bodyParts[.neck]!.location.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height)) .applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height)))
.frame(width: 15, height: 15)
}
}
}
}
}

Swift animating view height

I am trying to chain 2 animations. But can not get the second animation (change of height) working.
1st try:
self.customView.frame = CGRect(x: 20, y: 0, width: width, height: 76)
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
self.customView.frame = CGRect(x: 20, y: 20, width: 280, height: 76)
}, completion: { (finished: Bool) in
UIView.animate(withDuration: 0.3) {
self.customView.frame = CGRect(x: 20, y: 20, width: 280, height: 136)
}
})
2nd one:
self.customView.frame = CGRect(x: 20, y: 0, width: width, height: 76)
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
self.customView.frame = CGRect(x: 20, y: 20, width: 280, height: 76)
}, completion: { (finished: Bool) in
UIView.animate(withDuration: 0.3) {
var heightConstraint = self.customView.heightAnchor.constraint(equalToConstant: 136)
heightConstraint.isActive = true
self.view.layoutIfNeeded()
}
})

plotly R graph - marker symbol doesn't show in legend unless segment trace is added

I have a plotly graph with the following:
- line plot where censored values are plotted as open circles, and non-censored dataset points are solid.
- in some graphs a trend line will be shown (added using add_segment)
I can't seem to get the marker legend to show up when the trend line is not added.
Ideally, I would like only the 'censored datapoints' to show up in the legend, so I have added two traces for markers (1 for censored with showlegend = TRUE, the other with showlegend=FALSE). Not sure if there is another way to do this - very new to plot_ly.
# datasets
results <- structure(list(slope = 0, slope_p_val = 0.672652383888971, int_from_data = 0.06,
pct_cens = 9.3, annual_slope_units = 0, sigclass = "No evidence of trend",
sig = structure(2L, .Label = c("Significant", "Not significant"
), class = "factor"), slope_text = " ", trend_color = "#CBCBCB"), class = "data.frame", row.names = c(NA,
-1L))
dataset <- dput(dataset)
structure(list(Date = structure(c(12794, 12823, 12863, 12893,
12921, 12948, 12978, 13003, 13048, 13073, 13108, 13137, 13172,
13199, 13230, 13263, 13291, 13318, 13349, 13375, 13405, 13432,
13472, 13486, 13523, 13564, 13592, 13622, 13648, 13683, 13705,
13746, 13775, 13810, 13838, 13852, 13929, 13957, 13986, 14014,
14053, 14067, 14110, 14139, 14166, 14196, 14224, 14266, 14294,
14321, 14348, 14377, 14405, 14446, 14476, 14501, 14532, 14566,
14593, 14636, 14684, 14712, 14740, 14770, 14811, 14839, 14868,
14896, 14929, 14952, 14993, 15020, 15050, 15077, 15105, 15146,
15174, 15208, 15238, 15265, 15293, 15315, 15350, 15385, 15412,
15441, 15482, 15511, 15537, 15566, 15600, 15631, 15658, 15685,
15728, 15742, 15769, 15811, 15839, 15868, 15904, 15931, 15958,
16001, 16030, 16042, 16091, 16119, 16149, 16174, 16204, 16230,
16268, 16302, 16330, 16359, 16386, 16412), class = "Date"), cenTF = c(FALSE,
FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE,
FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE), Value = c(0.05,
0.06, 0.025, 0.08, 0.06, 0.07, 0.05, 0.025, 0.025, 0.1, 0.12,
0.18, 0.14, 0.19, 0.36, 0.17, 0.09, 0.07, 0.05, 0.025, 0.05,
0.025, 0.08, 0.05, 0.06, 0.06, 0.06, 0.05, 0.07, 0.06, 0.05,
0.07, 0.06, 0.05, 0.05, 0.06, 0.05, 0.07, 0.1, 0.09, 0.025, 0.07,
0.07, 0.14, 0.17, 0.11, 0.1, 0.14, 0.17, 0.17, 0.18, 0.09, 0.08,
0.08, 0.1, 0.07, 0.07, 0.06, 0.025, 0.09, 0.07, 0.08, 0.06, 0.06,
0.06, 0.08, 0.06, 0.06, 0.05, 0.05, 0.06, 0.06, 0.11, 0.1, 0.1,
0.05, 0.06, 0.025, 0.06, 0.025, 0.06, 0.06, 0.07, 0.06, 0.05,
0.07, 0.05, 0.06, 0.05, 0.05, 0.06, 0.06, 0.025, 0.05, 0.06,
0.06, 0.06, 0.23, 0.06, 0.06, 0.06, 0.025, 0.05, 0.05, 0.1, 0.06,
0.06, 0.06, 0.07, 0.08, 0.06, 0.07, 0.06, 0.05, 0.07, 0.06, 0.06,
0.06)), row.names = c(NA, -118L), class = "data.frame")
plotly_1 <- function(dataset, results,
cen_var="cenTF", val_var="Value",
date_period =NULL
){
dataset <- dataset %>%
mutate(cenNM = case_when(!!ensym(cen_var) := FALSE ~ " ",
!!ensym(cen_var) := TRUE ~ "Value too low to detect"),
plotVal = !!ensym(val_var),
plotCen = !!ensym(cen_var),
pct_cens = round(sum(.data$plotCen, na.rm=TRUE)/sum(!is.na(.data$Value))*100, 1))
#PLOTTING PARAMETERS
legendFont <- list(
family = "sans-serif",
size = 14,
color = "#000")
#font for axis
axisFont <- list(
family = "sans-serif",
size = 17,
color = "#000")
#calculate trend line limits - confirm same fopr decdate as date...
min_x <- date_period[1]
max_x <- date_period[2]+1
min_y <- results$slope *min_x + results$int_from_data
max_y <- results$slope*max_x + results$int_from_data
#CREATE PLOT
p <- plot_ly(dataset, x = ~Date, y = ~plotVal,
color=I("#3182BD"),
type='scatter',
mode='lines',
showlegend=FALSE,
hoverinfo="none"
) %>%
add_markers(data=dataset %>% filter(plotCen == FALSE),
x = ~Date,y = ~plotVal,
color=I("#3182BD"),
symbol=I("circle"),
size=1,
showlegend=FALSE,
hoverinfo="text",
text= ~paste("Value:",plotVal,"<br>Date:",Date,"<br>Censored:",plotCen)
) %>%
add_markers(data=dataset %>% filter(plotCen == TRUE),
x = ~Date,y = ~plotVal,
size = 1,
symbol = I("circle-open"),
color=I("#3182BD"),
showlegend=TRUE,
name="Value too low to detect",
hoverinfo="text",
text=~paste("Value:",plotVal,"<br>Date:",Date,"<br>Censored:",plotCen)
)
#add trend line for significant trends only...
if(!any(results$slope_pval > alpha,results$slope == 0)){
p <- p %>%
add_segments(data=results,
x=min_x , xend=max_x, y=min_y , yend=max_y,
color="#FCBA19",
name="Long-term trend",
showlegend=TRUE,
inherit=TRUE)
}
return(p)
}
tmp <- plotly_1(dataset, results, date_period = c(2005,2014))
#glimpse(tmp)
tmp
The above does not show the markers in the legend, but
if I comment out the if(!any(results$slope_pval > alpha,results$slope == 0)) statement, the censored marker does show in the legend (as desired)
The problem is, that you are setting showlegend = FALSE in the plot_ly call, which has a global effect on the plot. If you add another add_lines instead of passing the data for the line trace directly in plot_ly you get the desired result:
library(plotly)
library(dplyr)
# datasets
alpha <- 0.6 # not defined in question
results <- structure(list(slope = 0, slope_p_val = 0.672652383888971, int_from_data = 0.06,
pct_cens = 9.3, annual_slope_units = 0, sigclass = "No evidence of trend",
sig = structure(2L, .Label = c("Significant", "Not significant"
), class = "factor"), slope_text = " ", trend_color = "#CBCBCB"), class = "data.frame", row.names = c(NA, -1L))
dataset <- structure(list(Date = structure(c(12794, 12823, 12863, 12893,
12921, 12948, 12978, 13003, 13048, 13073, 13108, 13137, 13172,
13199, 13230, 13263, 13291, 13318, 13349, 13375, 13405, 13432,
13472, 13486, 13523, 13564, 13592, 13622, 13648, 13683, 13705,
13746, 13775, 13810, 13838, 13852, 13929, 13957, 13986, 14014,
14053, 14067, 14110, 14139, 14166, 14196, 14224, 14266, 14294,
14321, 14348, 14377, 14405, 14446, 14476, 14501, 14532, 14566,
14593, 14636, 14684, 14712, 14740, 14770, 14811, 14839, 14868,
14896, 14929, 14952, 14993, 15020, 15050, 15077, 15105, 15146,
15174, 15208, 15238, 15265, 15293, 15315, 15350, 15385, 15412,
15441, 15482, 15511, 15537, 15566, 15600, 15631, 15658, 15685,
15728, 15742, 15769, 15811, 15839, 15868, 15904, 15931, 15958,
16001, 16030, 16042, 16091, 16119, 16149, 16174, 16204, 16230,
16268, 16302, 16330, 16359, 16386, 16412), class = "Date"),
cenTF = c(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE,
FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE),
Value = c(0.05, 0.06, 0.025, 0.08, 0.06, 0.07, 0.05, 0.025, 0.025, 0.1, 0.12,
0.18, 0.14, 0.19, 0.36, 0.17, 0.09, 0.07, 0.05, 0.025, 0.05,
0.025, 0.08, 0.05, 0.06, 0.06, 0.06, 0.05, 0.07, 0.06, 0.05,
0.07, 0.06, 0.05, 0.05, 0.06, 0.05, 0.07, 0.1, 0.09, 0.025, 0.07,
0.07, 0.14, 0.17, 0.11, 0.1, 0.14, 0.17, 0.17, 0.18, 0.09, 0.08,
0.08, 0.1, 0.07, 0.07, 0.06, 0.025, 0.09, 0.07, 0.08, 0.06, 0.06,
0.06, 0.08, 0.06, 0.06, 0.05, 0.05, 0.06, 0.06, 0.11, 0.1, 0.1,
0.05, 0.06, 0.025, 0.06, 0.025, 0.06, 0.06, 0.07, 0.06, 0.05,
0.07, 0.05, 0.06, 0.05, 0.05, 0.06, 0.06, 0.025, 0.05, 0.06,
0.06, 0.06, 0.23, 0.06, 0.06, 0.06, 0.025, 0.05, 0.05, 0.1, 0.06,
0.06, 0.06, 0.07, 0.08, 0.06, 0.07, 0.06, 0.05, 0.07, 0.06, 0.06,
0.06)), row.names = c(NA, -118L), class = "data.frame")
plotly_1 <- function(dataset,
results,
cen_var = "cenTF",
val_var = "Value",
date_period = NULL) {
dataset <- dataset %>%
mutate(
cenNM = case_when(
!!ensym(cen_var) := FALSE ~ " ",!!ensym(cen_var) := TRUE ~ "Value too low to detect"
),
plotVal = !!ensym(val_var),
plotCen = !!ensym(cen_var),
pct_cens = round(sum(.data$plotCen, na.rm = TRUE) / sum(!is.na(.data$Value)) *
100, 1)
)
#PLOTTING PARAMETERS
legendFont <- list(family = "sans-serif",
size = 14,
color = "#000")
#font for axis
axisFont <- list(family = "sans-serif",
size = 17,
color = "#000")
#calculate trend line limits - confirm same fopr decdate as date...
min_x <- date_period[1]
max_x <- date_period[2] + 1
min_y <- results$slope * min_x + results$int_from_data
max_y <- results$slope * max_x + results$int_from_data
#CREATE PLOT
p <- plot_ly(dataset,
type = 'scatter',
mode = 'lines',
hoverinfo = "none") %>%
add_lines(
x = ~ Date,
y = ~ plotVal,
color = I("#3182BD"),
showlegend = FALSE
) %>%
add_markers(
data = dataset %>% filter(plotCen == FALSE),
x = ~ Date,
y = ~ plotVal,
color = I("#3182BD"),
symbol = I("circle"),
size = 1,
showlegend = FALSE,
hoverinfo = "text",
text = ~ paste(
"Value:",
plotVal,
"<br>Date:",
Date,
"<br>Censored:",
plotCen
)
) %>%
add_markers(
data = dataset %>% filter(plotCen == TRUE),
x = ~ Date,
y = ~ plotVal,
size = 1,
symbol = I("circle-open"),
color = I("#3182BD"),
showlegend = TRUE,
name = "Value too low to detect",
hoverinfo = "text",
text = ~ paste(
"Value:",
plotVal,
"<br>Date:",
Date,
"<br>Censored:",
plotCen
)
)
# add trend line for significant trends only...
if (!any(results$slope_p_val > alpha, results$slope == 0)) {
p <- p %>%
add_segments(
data = results,
x = min_x ,
xend = max_x,
y = min_y ,
yend = max_y,
color = "#FCBA19",
name = "Long-term trend",
showlegend = TRUE,
inherit = TRUE
)
}
return(p)
}
tmp <- plotly_1(dataset, results, date_period = c(2005, 2014))
#glimpse(tmp)
tmp
Two more comments:
added alpha <- 0.6 as it wasn't defined.
Changed if(!any(results$slope_pval > alpha,results$slope == 0)){ to results$slope_p_val

peak detection for growing time series using Swift

Would anyone have a good algorithm to measure peaks in growing time series data using Swift (v3)? So, detect peaks as the data is streaming in.
E.g. a Swift version of the smooth z-wave algorithm. That algorithm seems to be suitable.
I would need to detect the peaks as shown below. The data contains positive and negative numbers. Output should be a counter of the peaks, and/or true/false for that specific sample.
Sample dataset (summary of the last series):
let samples = [0.01, -0.02, -0.02, 0.01, -0.01, -0.01, 0.00, 0.10, 0.31,
-0.10, -0.73, -0.68, 0.21, 1.22, 0.67, -0.59, -1.04, 0.06, 0.42, 0.07,
0.03, -0.18, 0.11, -0.06, -0.02, 0.16, 0.21, 0.03, -0.68, -0.89, 0.18,
1.31, 0.66, 0.07, -1.62, -0.16, 0.67, 0.19, -0.42, 0.23, -0.05, -0.01,
0.03, 0.06, 0.27, 0.15, -0.50, -1.18, 0.11, 1.30, 0.93, 0.16, -1.32,
-0.10, 0.55, 0.23, -0.03, -0.23, 0.16, -0.04, 0.01, 0.12, 0.35, -0.38,
-1.11, 0.07, 1.46, 0.61, -0.68, -1.16, 0.29, 0.54, -0.05, 0.02, -0.01,
0.12, 0.23, 0.29, -0.75, -0.95, 0.11, 1.51, 0.70, -0.30, -1.48, 0.13,
0.50, 0.18, -0.06, -0.01, -0.02, 0.03, -0.02, 0.06, 0.03, 0.03, 0.02,
-0.01, 0.01, 0.02, 0.01]
Update: Thanks to Jean-Paul for the initial Swift port. But not sure the z-wave algo is the right one for this dataset. lag=10,threshold=3,influence=0.2 works fine for the last series of the dataset, but I have not been able to find a combination that comes close for the complete dataset.
The issues: with a big lag the first data samples are not included, I need one signal per peak and the algorithm would need further work to be made more efficient.
E.g. result for full dataset, using the Python code, and (e.g.) lag=5,threshold=2.5,influence=0.7 is missing peaks for series 1 and 2, and showing too many false positives in the quiet periods:
Full dataset (should result in 25 peaks):
let samples = [-1.38, -0.97, -1.20, -2.06, -2.26, -0.99, 0.11, -0.47, -0.95, -2.61, -0.88, -0.74, -1.12, -1.19, -1.12, -1.04, -0.72, -1.21, -2.61, -1.41, -0.23, -0.27, -0.43, -1.77, -2.75, -0.61, -0.73, -1.53, -1.02, -1.14, -1.12, -1.06, -0.78, -0.72, -2.41, -1.55, -0.01, -0.44, -0.47, -2.02, -1.66, -0.43, -0.93, -1.51, -0.86, -1.06, -1.10, -0.88, -0.84, -1.26, -2.59, -0.92, 0.29, -0.50, -1.31, -2.40, -0.88, -0.56, -1.09, -1.14, -1.09, -0.90, -0.99, -0.84, -0.75, -2.59, -1.34, -0.08, -0.36, -0.50, -1.89, -1.60, -0.55, -0.78, -1.46, -0.96, -0.97, -1.18, -0.98, -1.10, -1.07, -1.06, -1.79, -1.78, -1.54, -1.25, -1.00, -0.46, -0.27, -0.20, -0.15, -0.13, -0.11, -0.13, -0.09, -0.09, -0.05, 0.02, 0.20, -0.31, -1.35, -0.03, 1.34, 0.52, 0.80, -0.91, -1.26, -0.10, -0.10, 0.53, 0.93, 0.60, -0.83, -1.87, -0.21, 1.26, 0.44, 0.86, 0.73, -2.05, -1.66, 0.31, 1.04, 0.72, 0.63, -0.01, -2.14, -0.48, 0.77, 0.63, 0.58, 0.66, -1.01, -1.28, 0.18, 0.44, 0.09, -0.27, -0.06, 0.06, -0.18, -0.01, -0.08, -0.07, -0.06, -0.06, -0.07, -0.07, -0.06, -0.05, -0.04, -0.03, -0.02, -0.02, -0.03, -0.03, -0.01, 0.01, 0.00, 0.01, 0.05, 0.12, 0.16, 0.25, 0.29, -0.16, -0.69, -1.05, -0.84, -0.54, -0.07, 0.46, 1.12, 1.05, 0.77, 0.68, 0.63, 0.39, -0.96, -1.61, -0.68, -0.14, -0.03, 0.22, 0.31, 0.15, -0.02, 0.11, 0.14, 0.00, 0.04, 0.18, 0.27, 0.14, -0.05, -0.03, -0.08, -0.41, -0.94, -1.03, -0.50, 0.02, 0.52, 1.10, 1.03, 0.79, 0.69, 0.55, -0.34, -1.17, -0.89, -0.54, -0.22, 0.37, 0.47, 0.39, 0.23, 0.00, -0.02, 0.05, 0.10, 0.12, 0.09, 0.05, -0.12, -0.50, -0.89, -0.89, -0.48, 0.00, 0.43, 1.03, 0.95, 0.67, 0.64, 0.47, -0.07, -0.85, -1.02, -0.73, -0.08, 0.38, 0.46, 0.32, 0.15, 0.01, -0.01, 0.09, 0.20, 0.23, 0.19, 0.12, -0.50, -1.17, -0.97, -0.12, 0.15, 0.70, 1.31, 0.97, 0.45, 0.27, -0.73, -1.00, -0.52, -0.27, 0.10, 0.33, 0.34, 0.23, 0.07, -0.04, -0.27, -0.24, 0.10, 0.21, 0.05, -0.07, 0.04, 0.21, 0.29, 0.16, -0.45, -1.13, -0.93, -0.28, 0.04, 0.72, 1.35, 1.05, 0.56, 0.43, 0.17, -0.59, -1.38, -0.76, 0.10, 0.44, 0.46, 0.35, 0.12, -0.07, -0.05, -0.01, -0.07, -0.04, 0.01, 0.01, 0.06, 0.02, -0.03, -0.05, 0.00, 0.01, -0.02, -0.03, -0.02, -0.01, 0.00, -0.01, 0.00, -0.01, 0.00, -0.01, -0.01, 0.00, 0.01, -0.01, -0.01, 0.00, 0.00, 0.01, 0.01, 0.01, 0.04, 0.06, 0.05, 0.05, 0.04, 0.03, 0.00, -0.12, -0.16, -0.09, -0.01, 0.14, 0.07, 0.06, 0.00, -0.03, 0.00, 0.06, 0.06, -0.04, -0.11, -0.02, 0.13, 0.18, 0.21, 0.01, -0.31, -0.92, -1.35, -0.62, 0.03, 0.78, 1.36, 1.07, 0.59, 0.75, 0.42, -1.65, -3.16, -0.97, 0.24, 1.44, 1.50, 0.84, 0.47, 0.56, 0.40, -1.50, -2.71, -1.22, 0.01, 1.20, 1.55, 0.92, 0.44, 0.66, 0.73, -0.43, -2.34, -2.28, -0.72, 0.36, 1.41, 1.56, 0.89, 0.54, 0.67, 0.39, -1.78, -2.75, -1.07, -0.07, 1.16, 1.65, 0.80, 0.47, 0.73, 0.86, -0.24, -1.52, -1.68, -0.39, 0.02, 0.38, 0.60, 0.49, 0.02, -0.42, -0.31, -0.01, 0.08, 0.00, -0.07, -0.05, -0.01, -0.02, -0.04, -0.05, -0.02, -0.01, -0.02, -0.02, -0.03, -0.05, -0.04, -0.03, -0.01, -0.01, 0.00, -0.01, 0.00, 0.01, 0.00, 0.00, 0.00, 0.01, 0.01, -0.01, -0.03, -0.02, -0.01, 0.00, 0.00, 0.00, -0.01, 0.01, 0.00, -0.01, 0.02, 0.07, 0.15, 0.28, 0.31, 0.08, -0.26, -0.54, -0.96, -1.08, -0.27, 0.01, 0.45, 1.18, 1.07, 0.71, 0.65, 0.20, -0.80, -1.30, -0.74, -0.24, 0.29, 0.47, 0.34, 0.15, 0.02, 0.03, -0.02, -0.16, -0.13, 0.05, 0.09, -0.01, -0.08, -0.06, 0.03, 0.13, 0.19, 0.23, 0.18, 0.10, -0.07, -0.44, -0.91, -1.05, -0.64, -0.08, 0.50, 1.12, 1.35, 0.89, 0.58, 0.54, -0.58, -1.27, -1.20, -0.48, 0.19, 0.62, 0.62, 0.37, -0.01, -0.35, -0.33, 0.07, 0.29, 0.10, -0.14, -0.10, 0.07, 0.07, 0.01, 0.03, 0.09, 0.20, 0.32, 0.26, -0.02, -0.32, -0.78, -1.25, -0.93, -0.16, 0.30, 0.88, 1.40, 1.14, 0.72, 0.48, -0.54, -1.21, -1.13, -0.41, 0.18, 0.51, 0.53, 0.36, 0.11, -0.03, -0.09, -0.28, -0.11, 0.11, 0.15, 0.04, -0.08, -0.04, 0.04, 0.09, 0.16, 0.26, 0.43, 0.09, -0.88, -1.46, -0.64, -0.16, 0.43, 1.37, 1.34, 0.84, 0.52, -0.17, -0.87, -1.22, -0.76, 0.03, 0.47, 0.60, 0.36, 0.04, -0.09, -0.03, 0.02, -0.04, 0.04, 0.12, 0.13, 0.19, 0.27, 0.31, 0.18, -0.42, -0.99, -1.13, -0.75, -0.22, 0.50, 1.42, 1.41, 0.98, 0.51, 0.29, -0.69, -1.59, -0.88, -0.13, 0.31, 0.49, 0.46, 0.30, 0.05, -0.08, -0.03, 0.01, -0.04, -0.06, 0.02, 0.03, 0.01, -0.02, 0.01, 0.04, 0.06, 0.04, 0.03, 0.02, 0.03, 0.03, 0.01, -0.01, 0.00, 0.02, 0.00, 0.02, 0.02, 0.02, -0.02, -0.01, 0.02, 0.02, 0.01, 0.02, 0.02, 0.02, 0.02, 0.04, 0.03, 0.01, 0.01, 0.02, 0.01, 0.01, 0.01, 0.02, 0.01, 0.00, 0.01, 0.01, 0.00, 0.00, 0.01, 0.00, 0.00, 0.01, 0.00, 0.02, 0.00, 0.00, 0.01, 0.01, 0.00, 0.00, 0.01, 0.01, 0.00, 0.00, 0.00, 0.01, 0.01, 0.00, 0.01, 0.00, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.01, 0.01, 0.01, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00]
I am therefore not sure the z-wave algorithm is the right approach for this kind of dataset.
Translation of smooth z-score algo into Swift
Well, to quickly help you out: here is a translation of the algo into Swift: Demo in Swift Sandbox
Warning: I am by no means a swift programmer, so there could be mistakes in there!
Also note that I have turned off negative signals, as for OP's purpose we only want positive signals.
Swift code:
import Glibc // or Darwin/ Foundation/ Cocoa/ UIKit (depending on OS)
// Function to calculate the arithmetic mean
func arithmeticMean(array: [Double]) -> Double {
var total: Double = 0
for number in array {
total += number
}
return total / Double(array.count)
}
// Function to calculate the standard deviation
func standardDeviation(array: [Double]) -> Double
{
let length = Double(array.count)
let avg = array.reduce(0, {$0 + $1}) / length
let sumOfSquaredAvgDiff = array.map { pow($0 - avg, 2.0)}.reduce(0, {$0 + $1})
return sqrt(sumOfSquaredAvgDiff / length)
}
// Function to extract some range from an array
func subArray<T>(array: [T], s: Int, e: Int) -> [T] {
if e > array.count {
return []
}
return Array(array[s..<min(e, array.count)])
}
// Smooth z-score thresholding filter
func ThresholdingAlgo(y: [Double],lag: Int,threshold: Double,influence: Double) -> ([Int],[Double],[Double]) {
// Create arrays
var signals = Array(repeating: 0, count: y.count)
var filteredY = Array(repeating: 0.0, count: y.count)
var avgFilter = Array(repeating: 0.0, count: y.count)
var stdFilter = Array(repeating: 0.0, count: y.count)
// Initialise variables
for i in 0...lag-1 {
signals[i] = 0
filteredY[i] = y[i]
}
// Start filter
avgFilter[lag-1] = arithmeticMean(array: subArray(array: y, s: 0, e: lag-1))
stdFilter[lag-1] = standardDeviation(array: subArray(array: y, s: 0, e: lag-1))
for i in lag...y.count-1 {
if abs(y[i] - avgFilter[i-1]) > threshold*stdFilter[i-1] {
if y[i] > avgFilter[i-1] {
signals[i] = 1 // Positive signal
} else {
// Negative signals are turned off for this application
//signals[i] = -1 // Negative signal
}
filteredY[i] = influence*y[i] + (1-influence)*filteredY[i-1]
} else {
signals[i] = 0 // No signal
filteredY[i] = y[i]
}
// Adjust the filters
avgFilter[i] = arithmeticMean(array: subArray(array: filteredY, s: i-lag, e: i))
stdFilter[i] = standardDeviation(array: subArray(array: filteredY, s: i-lag, e: i))
}
return (signals,avgFilter,stdFilter)
}
// Demo
let samples = [0.01, -0.02, -0.02, 0.01, -0.01, -0.01, 0.00, 0.10, 0.31,
-0.10, -0.73, -0.68, 0.21, 1.22, 0.67, -0.59, -1.04, 0.06, 0.42, 0.07,
0.03, -0.18, 0.11, -0.06, -0.02, 0.16, 0.21, 0.03, -0.68, -0.89, 0.18,
1.31, 0.66, 0.07, -1.62, -0.16, 0.67, 0.19, -0.42, 0.23, -0.05, -0.01,
0.03, 0.06, 0.27, 0.15, -0.50, -1.18, 0.11, 1.30, 0.93, 0.16, -1.32,
-0.10, 0.55, 0.23, -0.03, -0.23, 0.16, -0.04, 0.01, 0.12, 0.35, -0.38,
-1.11, 0.07, 1.46, 0.61, -0.68, -1.16, 0.29, 0.54, -0.05, 0.02, -0.01,
0.12, 0.23, 0.29, -0.75, -0.95, 0.11, 1.51, 0.70, -0.30, -1.48, 0.13,
0.50, 0.18, -0.06, -0.01, -0.02, 0.03, -0.02, 0.06, 0.03, 0.03, 0.02,
-0.01, 0.01, 0.02, 0.01]
// Run filter
let (signals,avgFilter,stdFilter) = ThresholdingAlgo(y: samples, lag: 10, threshold: 3, influence: 0.2)
// Print output to console
print("\nOutput: \n ")
for i in 0...signals.count - 1 {
print("Data point \(i)\t\t sample: \(samples[i]) \t signal: \(signals[i])\n")
}
// Raw data for creating a plot in Excel
print("\n \n Raw data for creating a plot in Excel: \n ")
for i in 0...signals.count - 1 {
print("\(i+1)\t\(samples[i])\t\(signals[i])\t\(avgFilter[i])\t\(stdFilter[i])\n")
}
With the result for the sample data (for lag = 10, threshold = 3, influence = 0.2):
Update
You can improve the performance of the algorithm by using different values for the lag of the mean and the standard deviation. E.g.:
// Smooth z-score thresholding filter
func ThresholdingAlgo(y: [Double], lagMean: Int, lagStd: Int, threshold: Double, influenceMean: Double, influenceStd: Double) -> ([Int],[Double],[Double]) {
// Create arrays
var signals = Array(repeating: 0, count: y.count)
var filteredYmean = Array(repeating: 0.0, count: y.count)
var filteredYstd = Array(repeating: 0.0, count: y.count)
var avgFilter = Array(repeating: 0.0, count: y.count)
var stdFilter = Array(repeating: 0.0, count: y.count)
// Initialise variables
for i in 0...lagMean-1 {
signals[i] = 0
filteredYmean[i] = y[i]
filteredYstd[i] = y[i]
}
// Start filter
avgFilter[lagMean-1] = arithmeticMean(array: subArray(array: y, s: 0, e: lagMean-1))
stdFilter[lagStd-1] = standardDeviation(array: subArray(array: y, s: 0, e: lagStd-1))
for i in max(lagMean,lagStd)...y.count-1 {
if abs(y[i] - avgFilter[i-1]) > threshold*stdFilter[i-1] {
if y[i] > avgFilter[i-1] {
signals[i] = 1 // Positive signal
} else {
signals[i] = -1 // Negative signal
}
filteredYmean[i] = influenceMean*y[i] + (1-influenceMean)*filteredYmean[i-1]
filteredYstd[i] = influenceStd*y[i] + (1-influenceStd)*filteredYstd[i-1]
} else {
signals[i] = 0 // No signal
filteredYmean[i] = y[i]
filteredYstd[i] = y[i]
}
// Adjust the filters
avgFilter[i] = arithmeticMean(array: subArray(array: filteredYmean, s: i-lagMean, e: i))
stdFilter[i] = standardDeviation(array: subArray(array: filteredYstd, s: i-lagStd, e: i))
}
return (signals,avgFilter,stdFilter)
}
Then using for example let (signals,avgFilter,stdFilter) = ThresholdingAlgo(y: samples, lagMean: 10, lagStd: 100, threshold: 2, influenceMean: 0.5, influenceStd: 0.1) can give a lot better results:
DEMO

how to fill through color in CGContextStrokeLineSegments?

i am using the code to draw a triangle , but how to fill color in it.
CGContextSetRGBStrokeColor(c, 255, 0, 255, 1);
//CGContextSetRGBStrokeColor(c, 1.0, 1.0, 1.0, 1.0);
// Drawing with a blue fill color
CGContextSetRGBFillColor(c, 0.0, 0.8, 1.0, 1.0);
CGPoint points[6] = { CGPointMake(142, 200), CGPointMake(150, 250),
CGPointMake(150, 250), CGPointMake(135, 250),
CGPointMake(135, 250), CGPointMake(142, 200) };
CGContextStrokeLineSegments(c, points, 6);
First create a path, then fill it:
CGContextSetRGBFillColor(c, 0.0, 0.8, 1.0, 1.0);
CGPoint points[6] = { CGPointMake(142, 200), CGPointMake(150, 250),
CGPointMake(150, 250), CGPointMake(135, 250),
CGPointMake(135, 250), CGPointMake(142, 200) };
CGContextAddLines(c, points, 6);
CGContextClosePath(c);
CGContextFillPath(c);