Skip to content

Commit

Permalink
refactor: changed 'avg' to 'average'; finished unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathancallahan committed Jan 18, 2023
1 parent 91567ad commit 06cf426
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 51 deletions.
12 changes: 6 additions & 6 deletions dailyAverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { trimDate } from "./trimDate.js";
* Calculates daily averages for the time series specified by `datetime` and `x`.
*
* The returned object contains two properties:
* * datetime -- an array of date objects specifying the starting hour of each day
* * avg -- an array daily average values for each day
* * datetime -- Array of date objects specifying the starting hour of each day
* * average -- Array or daily average values for each day
* @param {...Date} datetime Regular hourly axis representing the time associated
* with each measurement.
* @param {...Number} x Array of hourly measurements.
Expand All @@ -21,19 +21,19 @@ export function dailyAverage(datetime, x, timezone) {

let dayCount = trimmed.datetime.length / 24;
let daily_datetime = [];
let daily_avg = [];
let daily_average = [];
for (let i = 0; i < dayCount; i++) {
let start = i * 24;
let end = i * 24 + 24;
// NOTE: Average is assigned to the start of the day.
daily_datetime[i] = trimmed.datetime[start];
daily_avg[i] = trimmed.x.slice(start, end).reduce((a, o) => a + o) / 24;
daily_average[i] = trimmed.x.slice(start, end).reduce((a, o) => a + o) / 24;
}

// Round to one decimal place and ensure null is the missing value
daily_avg = daily_avg.map((o) =>
daily_average = daily_average.map((o) =>
o === null || o === undefined || isNaN(o) ? null : Math.round(10 * o) / 10
);

return { datetime: daily_datetime, avg: daily_avg };
return { datetime: daily_datetime, average: daily_average };
}
10 changes: 5 additions & 5 deletions diurnalAverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { trimDate } from "./trimDate.js";
*
* The returned object contains two properties:
* * hour -- Array of local time hours [0-24]
* * avg -- Array of time-of-day averages for each hour
* * average -- Array of time-of-day averages for each hour
*
* By default, the averages are calculated using data from the most recent 7 days
* in the `datetime` array.
Expand All @@ -28,7 +28,7 @@ export function diurnalAverage(datetime, x, timezone, dayCount = 7) {
let hours = localTime.map((o) => o.hours());

let hour = [];
let hourly_avg = [];
let hourly_average = [];

// For each hour, average together the contributions from each day
for (let h = 0; h < 24; h++) {
Expand All @@ -37,13 +37,13 @@ export function diurnalAverage(datetime, x, timezone, dayCount = 7) {
sum += x[h + d * 24];
}
hour[h] = h;
hourly_avg[h] = sum / dayCount;
hourly_average[h] = sum / dayCount;
}

// Round to one decimal place and ensure null is the missing value
hourly_avg = hourly_avg.map((o) =>
hourly_average = hourly_average.map((o) =>
o === null || o === undefined || isNaN(o) ? null : Math.round(10 * o) / 10
);

return { hour: hour, avg: hourly_avg };
return { hour: hour, average: hourly_average };
}
49 changes: 49 additions & 0 deletions tests/dailyAverage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { test } from "uvu";
import * as assert from "uvu/assert";

import moment from "moment-timezone";
import { dailyAverage } from "../index.js";

// Start of Valentine's Day in Greenwich
let start = moment.tz("2023-02-14 00:00:00", "UTC");
let datetime = [];
let x = [];

// Precisely 10 days worth of data
let day = 0;
for (var i = 0; i < 240; i++) {
if (i % 24 === 0) day++;
datetime[i] = new Date(start + i * 3600 * 1000);
let val = day * 10 + 5 * Math.sin((i * Math.PI) / 12);
x[i] = Math.round(val * 10) / 10;
}

test("daily averages work for 'UTC'", () => {
let timezone = "UTC";
let daily = dailyAverage(datetime, x, timezone);
let day0 = new Date(moment.tz("2023-02-14 00:00:00", "UTC"));
let day1 = new Date(moment.tz("2023-02-15 00:00:00", "UTC"));
assert.equal(daily.datetime.slice(0, 2), [day0, day1]);
assert.equal(daily.average.slice(0, 2), [10, 20]);
});

test("daily averages work for 'America/Chicago'", () => {
let timezone = "America/Chicago";
let daily = dailyAverage(datetime, x, timezone);
let day0 = new Date(moment.tz("2023-02-14 06:00:00", "UTC"));
let day1 = new Date(moment.tz("2023-02-15 06:00:00", "UTC"));
assert.equal(daily.datetime.slice(0, 2), [day0, day1]);
assert.equal(daily.average.slice(0, 2), [12.5, 22.5]);
});

// ----- Run all tests ---------------------------------------------------------

test.run();

// ----- Notes -----------------------------------------------------------------

// // Manual testing for "America/Chicago"
// x.slice(6,30).reduce((a,o) => a + o) / 24
// // 12.5
// x.slice(30,54).reduce((a,o) => a + o) / 24
// // 22.5
23 changes: 0 additions & 23 deletions tests/demo.js

This file was deleted.

59 changes: 59 additions & 0 deletions tests/diurnalAverage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { test } from "uvu";
import * as assert from "uvu/assert";

import moment from "moment-timezone";
import { diurnalAverage } from "../index.js";

// Start of Valentine's Day in Greenwich
let start = moment.tz("2023-02-14 00:00:00", "UTC");
let datetime = [];
let x = [];

// Precisely 10 days worth of data
let day = 0;
for (var i = 0; i < 240; i++) {
if (i % 24 === 0) day++;
datetime[i] = new Date(start + i * 3600 * 1000);
let val = 10 + 5 * Math.sin((i * Math.PI) / 12);
x[i] = Math.round(val * 10) / 10;
}

let hours = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23,
];

test("diurnal averages work for 'UTC'", () => {
let timezone = "UTC";
let diurnal = diurnalAverage(datetime, x, timezone);
let averages = [
10, 11.3, 12.5, 13.5, 14.3, 14.8, 15, 14.8, 14.3, 13.5, 12.5, 11.3, 10, 8.7,
7.5, 6.5, 5.7, 5.2, 5, 5.2, 5.7, 6.5, 7.5, 8.7,
];
assert.equal(diurnal.hour, hours);
assert.equal(diurnal.averages);
});

test("diurnal averages work for 'America/Chicago'", () => {
let timezone = "America/Chicago";
let diurnal = diurnalAverage(datetime, x, timezone);
let averages = [
15, 14.8, 14.3, 13.5, 12.5, 11.3, 10, 8.7, 7.5, 6.5, 5.7, 5.2, 5, 5.2, 5.7,
6.5, 7.5, 8.7, 10, 11.3, 12.5, 13.5, 14.3, 14.8,
];
assert.equal(diurnal.hour, hours);
assert.equal(diurnal.averages);
});

// ----- Run all tests ---------------------------------------------------------

test.run();

// ----- Notes -----------------------------------------------------------------

// Manual testing for "America/Chicago"

// x.slice(0,24)
// // (24) [10, 11.3, 12.5, 13.5, 14.3, 14.8, 15, 14.8, 14.3, 13.5, 12.5, 11.3, 10, 8.7, 7.5, 6.5, 5.7, 5.2, 5, 5.2, 5.7, 6.5, 7.5, 8.7]
// x.slice(6,30)
// // (24) [15, 14.8, 14.3, 13.5, 12.5, 11.3, 10, 8.7, 7.5, 6.5, 5.7, 5.2, 5, 5.2, 5.7, 6.5, 7.5, 8.7, 10, 11.3, 12.5, 13.5, 14.3, 14.8]
16 changes: 0 additions & 16 deletions tests/interactive_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,4 @@ let z = 1;

// ----- PLAY AREA -------------------------------------------------------------

let start = moment.tz("2023-02-14 00:00:00", "UTC");
datetime = [];
x = [];

for (var i = 0; i < 240; i++) {
datetime[i] = new Date(start + i * 3600 * 1000);
let val = 10 + 5 * Math.sin((i * Math.PI) / 12) + Math.random() * 6 - 3;
x[i] = Math.round(val * 10) / 10;
}

for (var i = 0; i < 240; i++) {
datetime[i] = moment.tz(start + i * 3600 * 1000, "UTC");
let val = 10 + 5 * Math.sin((i * Math.PI) / 12) + Math.random() * 6 - 3;
x[i] = Math.round(val * 10) / 10;
}

let zzz = 1;
21 changes: 21 additions & 0 deletions tests/nowcast.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { test } from "uvu";
import * as assert from "uvu/assert";

import { pm_nowcast } from "../index.js";

test("nowcast works properly", () => {
// Data from: https://forum.airnowtech.org/t/the-nowcast-for-pm2-5-and-pm10/172
// Answer should be 28.4 ug/m3 or 85 AQI
let pm25 = [34.9, 43, 50, 64.9, 69.2, 66.2, 53.7, 48.6, 49.2, 35, null, 21];
let nowcast = pm_nowcast(pm25);
assert.is(nowcast.length, pm25.length);
assert.is(nowcast[0], null);
assert.is(nowcast[11], 28.4);
});

test("nowcast handles missing values properly", () => {
let pm25 = [34.9, 43, 50, 64.9, 69.2, 66.2, 53.7, 48.6, 49.2, 35, null, null];
assert.is(pm_nowcast(pm25)[11], null);
});

test.run();
17 changes: 17 additions & 0 deletions tests/trimDate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ for (var i = 0; i < 240; i++) {
x[i] = Math.round(val * 10) / 10;
}

test("trimming Date objects works for 'UTC'", () => {
let timezone = "UTC";
let trimmed = trimDate(datetime, x, timezone);
let tz_start = moment.tz(trimmed.datetime[0], timezone);
let tz_end = moment.tz(trimmed.datetime[239], timezone);
assert.is(trimmed.datetime.length, 240);
assert.is(tz_start.hours(), 0);
assert.is(tz_end.hours(), 23);
// UTC is 8 hours ahead of "America/Los_Angeles" during the winter
assert.is(trimmed.datetime[0].valueOf(), datetime[0].valueOf());
assert.is(trimmed.x[0], x[0]);
});

test("trimming Date objects works for 'America/Los_Angeles'", () => {
let timezone = "America/Los_Angeles";
let trimmed = trimDate(datetime, x, timezone);
Expand All @@ -28,6 +41,7 @@ test("trimming Date objects works for 'America/Los_Angeles'", () => {
assert.is(tz_end.hours(), 23);
// UTC is 8 hours ahead of "America/Los_Angeles" during the winter
assert.is(trimmed.datetime[0].valueOf(), datetime[8].valueOf());
assert.is(trimmed.x[0], x[8]);
});

test("trimming Date objects works for 'America/New_York'", () => {
Expand All @@ -40,6 +54,7 @@ test("trimming Date objects works for 'America/New_York'", () => {
assert.is(tz_end.hours(), 23);
// UTC is 5 hours ahead of "America/New_York" during the winter
assert.is(trimmed.datetime[0].valueOf(), datetime[5].valueOf());
assert.is(trimmed.x[0], x[5]);
});

// ----- Test passing in moment objects ----------------------------------------
Expand All @@ -61,6 +76,7 @@ test("trimming moment.tz objects works for 'America/Los_Angeles'", () => {
assert.is(tz_end.hours(), 23);
// UTC is 8 hours ahead of "America/Los_Angeles" during the winter
assert.is(trimmed.datetime[0].valueOf(), datetime[8].valueOf());
assert.is(trimmed.x[0], x[8]);
});

test("trimming Date objects works for 'America/New_York'", () => {
Expand All @@ -73,6 +89,7 @@ test("trimming Date objects works for 'America/New_York'", () => {
assert.is(tz_end.hours(), 23);
// UTC is 5 hours ahead of "America/New_York" during the winter
assert.is(trimmed.datetime[0].valueOf(), datetime[5].valueOf());
assert.is(trimmed.x[0], x[5]);
});

// ----- Run all tests ---------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion trimDate.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function trimDate(datetime, x, timezone) {
let start = hours[0] === 0 ? 0 : 24 - hours[0];
let end =
hours[hours.length - 1] === 23
? hours.length - 1
? hours.length
: hours.length - hours[hours.length - 1] - 1;

let trimmed_datetime = datetime.slice(start, end);
Expand Down

0 comments on commit 06cf426

Please sign in to comment.