Icon

Day3_​S1_​ok

1 · A value that cannot be true

A dead transmitter writes a number, not a blank.

Step 1 · Read the minute file and flag the impossible values


  1. CSV Reader reads day3_pump02_minute_v4.csv and String to Date&Time parses timestamp as yyyy-MM-dd HH:mm:ss: 173,549 rows, 0 failures
  2. One Expression node, four formulas evaluated in order. The first APPENDS flag_range = if(($bearing_temp_c ?? 50) < 20 or ($bearing_temp_c ?? 50) > 120 or ($flow_m3h ?? 1) < 0 or ($vib_cm_mm_s ?? 1) < 0, 1, 0)
  3. The other three REPLACE_EXISTING the offending cell only, e.g. if(($flow_m3h ?? 1) < 0, MISSING, $flow_m3h) - the flag is raised while the value is still readable
  4. Output of the stage: 173,549 rows, 12 of them carrying flag_range = 1 and one blank cell each. No row is deleted, so the two healthy tags in those rows stay usable
  5. The lower lane is a dead end that hangs off String to Date&Time, before any repair: Statistics shows the table exactly as the TS DB delivered it - 173,549 rows, 2024-12-01 00:00 to 2025-03-31 23:59, blank cells 45 / 45 / 120 on the three tags, and a minimum bearing_temp_c of -999.0 that is still sitting in the data at that point

2 · The same minute written twice

Only the later write is the corrected value.

Step 2 · Keep the corrected write, and remember which minute was rewritten


  1. Sorter orders by timestamp ascending, so the two writes of one minute end up adjacent
  2. Expression APPENDS flag_dup = if($["timestamp", -1] = $timestamp, 1, 0). The flag must be raised BEFORE the row goes: measured on this machine, Duplicate Row Filter writes no status column when it removes
  3. Duplicate Row Filter on timestamp, keep LAST: 173,549 rows become 173,449
  4. Output: one row per written minute, 100 of them carrying flag_dup = 1 all the way into the DQ report

3 · One row per minute, and what is behind each hole

Rows are not one minute apart, and the TS DB never writes an empty row.

Step 3 · Put every minute on the axis, then tell a slow logger from a stopped plant


  1. The lower lane measures the holes: Expression carries $["timestamp", -1] as ts_prev, Date&Time Difference gives gap_min in MINUTE, Row Filter keeps gap_min >= 2 and the Histogram shows 301 gaps: 2 min x172, 3 x64, 4 x32, 5 x30, then nothing at all until 26, 71 and 181
  2. Two cleanly separated populations - that measurement is what puts the sparse/outage cut at 5 minutes
  3. Date&Time Range Creator builds the 174,240 minute calendar (2024-12-01 00:00 to 2025-03-31 23:59, PT1M), Joiner LEFT with the calendar on port 1 leaves 791 minutes unmatched, Sorter puts the grid back in time order because the Joiner returns ARBITRARY
  4. Expression then builds cov = ($p_temp+$p_flow+$p_vib) / 3 and a 0/1 presence column, and two Moving Aggregator windows of 6 rows, one BACKWARD and one FORWARD, ask: is there a real reading within 5 minutes on this side? Output: Sum(pres_b) and Sum(pres_f) on all 174,240 rows

4 · A sensor that stopped moving

The value still arrives every minute. It just never changes.

Step 4 · Catch the stuck tag on the minute axis


  1. Expression APPENDS the minute-to-minute change of all three tags, e.g. d_flow = abs($flow_m3h - $["flow_m3h", -1])
  2. Moving Aggregator with windowLength 10 and windowType BACKWARD takes Sum and Count of d_flow, d_temp and d_vib. On the calendar 10 rows is exactly 10 minutes, and a window lying across a hole returns Count under 10
  3. Expression APPENDS flag_frozen = 1 when ($["Sum(d_flow)"] ?? 1) = 0 and ($["Count*(d_flow)"] ?? 0) = 10, or the same pair for d_temp or d_vib. Output: 190 minutes flagged, every one of them flow
  4. The lower lane proves it: Date&Time-based Row Filter cuts 14 Jan 02:00-07:00, 300 minutes, and the Line Plot shows flow flat at 161.45 m3/h for 200 of them, with normal signal on either side

5 · One label per minute

Resolve every flag into one verdict, then count it.

Step 5 · Turn the flags into one quality label per minute and count them


  1. Expression APPENDS quality = if($cov = 0, if($["Sum(pres_b)"] >= 1 and $["Sum(pres_f)"] >= 1, "sparse", "outage"), if($flag_frozen = 1, "frozen", if($cov < 0.34, "low_cov", "ok")))
  2. An empty minute with a real reading on BOTH sides within 5 minutes is sparse; with none on a side it is outage
  3. The same node APPENDS is_ok = if($flag_frozen = 0 and $cov >= 0.34, 1, 0), the one gate the hourly roll-up uses
  4. Output: ok 173,214 - sparse 516 - outage 275 - frozen 190 - low_cov 45 = 174,240. GroupBy on quality, flag_dup AND flag_range gives 7 rows, written by the CSV Writer to day3_s1_dq_report.csv

6 · Hand over to Session 2

Only the hours you can defend leave here - and you look at them before you trust them.

Step 6 · Roll up to hours, write the file, and read the three series


  1. Time Rounder APPENDS timestamp (hour) at HOURS_1 and Row Filter keeps is_ok = 1: 173,214 minutes. Nothing is imputed anywhere in this workflow
  2. GroupBy on timestamp (hour) gives 2,898 hours - Mean for temperature and flow, Maximum for vibration, a Count per tag - and Column Renamer writes back ts_hour, bearing_temp_c, flow_m3h, vib_cm_mm_s, n_temp, n_flow, n_vib, n_min
  3. Row Filter keeps n_temp, n_flow AND n_vib >= 30, per tag because n_min never sees an hour where one single tag is empty: 3 hours go and 2,895 rows reach day3_s1_hourly.csv. At 30 real minutes the hourly figure is off by at most 0.042 C, 0.29 m3/h and 0.033 mm/s
  4. Three Line Plot nodes on one lane, one tag per axis: 50.0-71.1 C, 103-184 m3/h and 2.16-5.38 mm/s cannot share a y axis. Temperature reads the renamed table (2,898 h, no blank cell), flow and vibration read the filtered 2,895 h; the three dropped hours are invisible at this scale
Node 1Read minute CSV173,549 rows
CSV Reader
Node 2Parse timestamp0 failed
String to Date&Time
Node 19Sum over 10 rows174,240 rows in
Moving Aggregator
Node 3Impossible to NA12 cells flagged
Expression
Node 4Profile raw tablen, blanks, min/max
Statistics
Node 5Order by time173,549 rows
Sorter
Node 6Mark rewritten min100 rows
Expression
Node 7Dedupe: keep last173,449 rows
Duplicate Row Filter
Node 20Flag a stuck tag190 min, all flow
Expression
Node 21Cut 5 h window300 min, 14 Jan
Date&Time-based Row Filter
Node 24Count by 3 keys7 rows
GroupBy
Node 25Write DQ report7 rows
CSV Writer
Node 22Flow per minute200 min flat
Line Plot
Node 23Label every minuteok 173,214
Expression
Node 28Roll up to hourly2,898 hours
GroupBy
Node 29Back to tag names9 columns
Column Renamer
Node 26Round to hour174,240 min in
Time Rounder
Node 27Keep ok minutes173,214 min
Row Filter
Node 31Write hourly file2,895 rows
CSV Writer
Node 10Keep the gaps301 rows >= 2 min
Row Filter
Node 32Temp per hour2,898 h, 50-71 C
Line Plot
Node 30Drop thin hours2,895 hours
Row Filter
Node 8Previous stamp173,449 rows
Expression
Node 13Calendar LEFT JOIN791 unmatched
Joiner
Node 9Gap from previousmax 181 min
Date&Time Difference
Node 14Order the grid174,240 rows
Sorter
Node 11Gap sizes5 min, then 26
Histogram
Node 33Flow per hour103 to 184 m3/h
Line Plot
Node 12Build the calendar174,240 min
Date&Time Range Creator
Node 34Vibration per hour2.16 to 5.38 mm/s
Line Plot
Node 17Look ahead 5 minwindow of 6 rows
Moving Aggregator
Node 18Change per minute3 delta columns
Expression
Node 15Presence and cov791 rows empty
Expression
Node 16Look back 5 minwindow of 6 rows
Moving Aggregator

Nodes

Extensions

Links