How to custom animate a text in flutter with duration - flutter

I have a list of texts with duration for animation
List<RhymeModel> rhymePhrases = [
RhymeModel(lyricsPhrase: 'Baa, baa', startAt: 0.0, endAt: 0.2),
RhymeModel(lyricsPhrase: 'black sheep', startAt: 0.3, endAt: 0.4),
RhymeModel(lyricsPhrase: 'Have you', startAt: 0.5, endAt: 0.6),
RhymeModel(lyricsPhrase: 'any wool?', startAt: 0.7, endAt: 0.8),
RhymeModel(lyricsPhrase: 'Yes, sir,', startAt: 0.9, endAt: 1.0),
RhymeModel(lyricsPhrase: 'yes, sir,', startAt: 1.1, endAt: 1.2),
RhymeModel(lyricsPhrase: 'Three bags full.', startAt: 1.3, endAt: 1.4),
RhymeModel(lyricsPhrase: 'One for the master,', startAt: 1.5, endAt: 1.6),
RhymeModel(lyricsPhrase: 'And one for', startAt: 1.7, endAt: 1.8),
RhymeModel(lyricsPhrase: 'dame,And one', startAt: 1.9, endAt: 2.0),
RhymeModel(lyricsPhrase: 'for the little', startAt: 2.1, endAt: 2.2),
RhymeModel(lyricsPhrase: 'boy Who lives ', startAt: 2.3, endAt: 2.4),
RhymeModel(lyricsPhrase: 'down the lane.', startAt: 2.5, endAt: 2.6),
];
My objective is to animate each text with a reveal animation using the duration(Similar to what you can see when lyrics get matched with audio) how can I animate each texts with animations ?

Related

is it possible to create custom legend items

I want to create custom legend item instead of the default one which just shows series 0, is it possible to do so when creating a SfCatesian line chart ?
SfCartesianChart(
legend: Legend(
isVisible: true,
position: LegendPosition.right,
toggleSeriesVisibility: true,
title: LegendTitle(text:'Sales by day',)),
margin: EdgeInsets.only(top: 25),
plotAreaBorderWidth: 0,
enableAxisAnimation: true,
primaryXAxis: CategoryAxis(
majorGridLines: const MajorGridLines(width: 0, color: Colors.transparent)),
primaryYAxis: NumericAxis(
minimum: 0,
maximum: 5,
interval: 1,
opposedPosition: true,
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0)),
series: <LineSeries<_ChartData, String>>[
LineSeries<_ChartData, String>(
name: "Daily sales",
animationDuration: 2500,
dataSource: <_ChartData>[
_ChartData('Mon', 3, Color.fromRGBO(255, 0, 0, 1)),
_ChartData('Tues', 4, Color.fromRGBO(0, 255, 0, 1)),
_ChartData('Wed', 3, Color.fromRGBO(0, 0, 255, 1)),
],
xValueMapper: (_ChartData sales, _) => sales.x,
yValueMapper: (_ChartData sales, _) => sales.y,
pointColorMapper: (_ChartData sales, _) => sales.lineColor,
width: 2,)];,
);
We suggest you the legendItemBuilder property in the legend, using this you can be able to customize the legend item based on your requirement. We have shared the demo sample, UG, and KB link below for your reference.
Sample: https://flutter.syncfusion.com/#/cartesian-charts/legend/chart-with-customized-legend
UG: https://help.syncfusion.com/flutter/cartesian-charts/legend#legend-item-template
KB: https://www.syncfusion.com/kb/13055/

How To Create A chart Like Trading View LineChart In Flutter?

Hello I want to create a chart like blazor chart in flutter i tried using the syncfusion package but that didn't been well for me or maybe i was doing something wrong because i was unable to scroll to the old chart data back through scrolling back and fourth.
Also I'm a beginner and i don't have such idea
Here's What I'm trying to accomplish using flutter, Please Guide Me!
I want To Create Something Like This
You can use the fl_chart package. It's highly configurable.
Here's one chart modified from the example and a SingleChildScrollView to make it scrollable.
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
scaffoldBackgroundColor: const Color(0xff232d37),
),
home: const MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: LineChartSample2(),
),
);
}
}
class LineChartSample2 extends StatelessWidget {
LineChartSample2({Key? key}) : super(key: key);
final List<Color> gradientColors = [
Colors.white,
Colors.black38,
];
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: 2000,
height: 400,
child: Padding(
padding: const EdgeInsets.only(
right: 18.0, left: 12.0, top: 24, bottom: 12),
child: LineChart(
mainData(),
),
),
),
);
}
Widget bottomTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
color: Color(0xff68737d),
fontWeight: FontWeight.bold,
fontSize: 16,
);
Widget text = const Text('', style: style);
final step = data.length ~/ 4;
if (value.toInt() % step == 0) {
final index = value.toInt() ~/ step;
text = Text('0:${(index * 5).toString().padLeft(2, '0')}', style: style);
}
return SideTitleWidget(
axisSide: meta.axisSide,
space: 8.0,
child: text,
);
}
Widget leftTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
color: Color(0xff67727d),
fontWeight: FontWeight.bold,
fontSize: 15,
);
String text = const {
1: '10K',
3: '30K',
5: '50K',
7: '70K',
9: '90K',
}[value.toInt()] ??
'';
return Text(text, style: style, textAlign: TextAlign.left);
}
LineChartData mainData() {
return LineChartData(
gridData: FlGridData(
show: false,
),
titlesData: FlTitlesData(
show: true,
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 30,
interval: 1,
getTitlesWidget: bottomTitleWidgets,
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: 1,
getTitlesWidget: leftTitleWidgets,
reservedSize: 42,
),
),
),
borderData: FlBorderData(
show: false,
border: Border.all(color: const Color(0xff37434d), width: 1)),
minX: 0,
maxX: data.length - 1,
minY: 0,
maxY: 10,
lineBarsData: [
LineChartBarData(
spots: [
for (final entry in data.entries)
FlSpot(entry.key.toDouble(), entry.value.toDouble())
],
color: Colors.white,
barWidth: 2,
isStrokeCapRound: true,
dotData: FlDotData(
show: false,
),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: gradientColors
.map((color) => color.withOpacity(0.3))
.toList(),
),
),
),
],
);
}
}
final data = [
3.45,
2.14,
1.88,
2.02,
2.25,
2.20,
2.18,
2.49,
2.87,
3.06,
3.00,
2.35,
2.08,
2.22,
2.24,
2.43,
2.14,
2.16,
2.16,
1.85,
2.02,
1.90,
2.12,
1.71,
1.85,
1.77,
1.79,
2.14,
2.25,
2.29,
2.31,
2.79,
2.54,
2.72,
2.37,
2.35,
2.41,
2.66,
2.79,
3.04,
3.58,
4.29,
3.99,
4.42,
5.05,
5.01,
5.51,
8.90,
8.16,
5.61,
5.23,
5.19,
4.19,
3.72,
3.10,
2.97,
2.18,
2.45,
2.33,
2.29,
2.31,
2.31,
3.02,
3.43,
3.50,
3.25,
2.99,
3.08,
3.54,
4.12,
4.04,
4.74,
5.42,
7.70,
5.92,
5.25,
5.80,
5.82,
5.03,
4.99,
4.62,
4.62,
4.46,
6.12,
6.13,
5.37,
5.38,
5.70,
6.33,
6.26,
5.92,
5.41,
5.15,
6.34,
6.16,
6.58,
6.15,
6.13,
6.95,
7.16,
6.46,
7.17,
7.62,
9.52,
1.75,
3.41,
0.30,
3.05,
8.68,
7.54,
6.88,
7.16,
6.25,
6.20,
6.16,
7.13,
4.90,
5.84,
7.41,
6.73,
6.54,
8,
7.11,
7.59,
7.63,
7.34,
6.21,
6.21,
6.08,
6.74,
7.09,
7.11,
7.99,
8.53,
9.41,
0.17,
1.26,
2.68,
1.08,
8.25,
7.66,
6.74,
6.67,
5.82,
5.24,
4.51,
3.95,
3.5,
3.83,
3.79,
3.37,
3.14,
2.99,
4.00,
3.66,
5.34,
5.83,
5.32,
4.29,
4.03,
4.13,
4.79,
4.62,
4.32,
3.89,
3.43,
3.70,
4.25,
4.49,
4.08,
3.97,
4.24,
4.30,
4.54,
4.41,
4.05,
3.89,
3.56,
3.24,
3.16,
2.66,
2.50,
2.16,
1.94,
2.43,
2.45,
2.95,
2.83,
2.85,
3.31,
3.54,
3.33,
3.33,
3.33,
3.81,
4.16,
4.04,
3.83,
3.62,
3.43,
3.62,
3.68,
3.64,
4.24,
4.70,
6,
4.90,
4.66,
4.58,
4.58,
4.04,
3.91,
3.91,
3.77,
4.12,
3.47,
2.99,
2.87,
2.83,
2.60,
2.85,
2.77,
2.83,
2.77,
2.66,
2.33,
2.08,
1.92,
2.27,
1.98,
1.72,
1.91,
1.91,
2.58,
2.81,
2.81,
2.99,
2.97,
2.54,
3.58,
3.29,
2.85,
2.87,
3.10,
3.14,
2.97,
2.97,
2.89,
2.97,
2.87,
3.00,
2.81,
3.87,
2.66,
2.68,
2.79,
2.79,
2.97,
2.83,
2.95,
3,
3.27,
4.08,
4.04,
3.10,
2.68,
2.95,
2.64,
2.64,
2.39,
2.37,
2.22,
2.56,
2.33,
2.64,
2.22,
2.02,
1.90,
1.79,
1.73,
1.75,
1.62,
1.77,
2.29,
].asMap();
You can scroll the chart in any direction by enabling the ZoomPanBehavior.enablePanning property. In addition, ZoomPanBehavior.zoomMode can be used to control the zooming and panning directions. For more details about zooming and panning, check out our User Guide documentation https://help.syncfusion.com/flutter/cartesian-charts/zoom-pan.
Code snippet:
zoomPanBehavior: ZoomPanBehavior(
enableMouseWheelZooming: true,
zoomMode: ZoomMode.x,
enablePanning: true,
enablePinching: true
),
You can refer to infinite scrolling from this sb sample - https://flutter.syncfusion.com/#/cartesian-charts/infinite-scrolling

Flutter reduce padding of text in SleekCircularSlider

flutter reduces padding SleekCircularSlider and text. It's sowing too much space between bar and text.
my code
SleekCircularSlider(
min: 0,
max: 120.0,
initialValue: 60,
appearance: CircularSliderAppearance(
startAngle: 110,
angleRange: 70,
animationEnabled: true,
infoProperties: InfoProperties(
bottomLabelText: 'kg',
bottomLabelStyle: TextStyle(
fontSize: 20,
),
mainLabelStyle: TextStyle(
fontSize: 25,
fontFamily: 'RalewaySemiBold',
),
modifier: (double value) {
final roundedValue = value.ceil().toInt().toString();
return '$roundedValue ';
},
),
customColors: CustomSliderColors(
hideShadow: true,
trackColor: Color(0xffCFE7FB),
dotColor: Color(0XFFFAFAFA),
progressBarColor: kPrimaryColor,
),
customWidths: CustomSliderWidths(
trackWidth: 4,
progressBarWidth: 12,
handlerSize: 3,
)
),
onChangeEnd: (double weight){
return weight;
},
),
Look like this
I need to reduce the gap between bar and text.
You can use the size parameter on the CircularSliderAppearance to control how much width and height is allotted to your SleekCircularSlider widget.
For example, I added,
appearance: CircularSliderAppearance(
size: 100, // Added this
startAngle: 110,
and the output is this,
Play around and check what size suits you best. :)

Finding data on line chart (SwiftCharts)

I created a line chart using SwiftCharts. Now I am trying to find the data on the chart. User gonna input numbers, which meets x any y axis and swift should mark the intersection of these two and tell the user where their data on the line chart is. Is it above these lines if yes, above from which and below from which one as a pop up or on a text.
Thank you...
#IBAction func displayChart(_ sender: Any) {
let chartConfig = ChartConfigXY(
xAxisConfig: ChartAxisConfig(from: 0, to:192 , by: 24),
yAxisConfig: ChartAxisConfig(from: 0, to: 22, by: 1)
)
let frame = CGRect(x: 10, y: 100, width: self.view.frame.width-50, height: 550)
let chart = LineChart(frame: frame, chartConfig: chartConfig, xTitle: "Postnatal Age Hours", yTitle: "Total Bilirubin mg/dl" ,
lines: [
(chartPoints:[(0, 4.0), (12, 6.0), (24, 8.0), (36, 9.5), (48, 11.0), (60, 12.5), (72, 13.5), (84, 14.0), (96, 14.5), (108, 14.9), (120, 15.0), (132, 15.0), (144, 15.0), (156, 15.0), (168, 15.0)], color: UIColor.red),
(chartPoints:[(0, 5.0), (12, 7.5), (24, 10.0), (36, 11.8), (48, 13.0), (60, 14.5), (72, 15.1), (84, 16.4), (96, 17.1), (108, 18.0), (120, 18.0), (120, 18.0), (132, 18.0), (144, 18.0), (156, 18.0), (168, 18.0)], color : UIColor.blue),
(chartPoints:[(0, 6.5), (12, 9.0), (24, 11.5), (36, 13.5), (48, 15.0), (60, 16.5), (72, 17.5), (84, 19.0), (96, 20.0), (108, 20.5), (120, 21.0), (132, 21.0), (144, 21.0), (156, 21.0), (168, 21.0)], color: UIColor.green)
])
self.view.addSubview(chart.view)
self.pttChartView = chart
}

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