-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Statistical functions added (4) #5
Conversation
DescriptionThe following functions have been added into hydro.js:
Type of change
How Has This Been Tested?Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Checklist:
|
DescriptionThe following functions have been added into hydro.js: Type of change
How Has This Been Tested?Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Checklist:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stats
General
- Keep consistency in the parameters for main functions. Close the parameters so that in case the user does not pass the parameter it defaults to an empty object. Example: `static linearMovingAverage ({params, args, data} = {})``
Linear Moving Average
- Parameter descriptor is wrong.
- In the first for loop, instead of creating a new array each time, keep a running sum that does the same job and is more efficient. Example:
static LinearMovingAverage({ params, args, data } = {}) {
...
const movingAverage = [];
let sum = 0;
for (let i = 0; i < windowSize; i++) {
sum += data[i];
}
movingAverage.push(sum / windowSize);
for (let i = windowSize; i < data.length; i++) {
sum += data[i] - data[i - windowSize];
movingAverage.push(sum / windowSize);
}
return movingAverage;
}
Differencing
- Improve the name or description. It is true that the function does differentiation, but it is used to make time series non stationary data into stationary. You can leave the name but improve description.
- The for loop is inefficient for large time series. Make the following change:
...
const differencedSeries = timeSeries.slice(order).map((value, i) => value - timeSeries[i]);
return differencedSeries
heteroskedasticity
- Calculating heteroskedasticity involves using other tests alongside the implementation of variance of residuals. Rename this function as
residualVariance
or something similar. The missing tests can also be implemented (White's test, Bresuch-Pagan, Goldfeld-Quandt). Example:
static residualVariance({ params, args, data } = {}) {
const residuals = data;
if (!Array.isArray(residuals)) {
throw new Error('Invalid data. Expecting an array of residuals.');
}
if (residuals.length < 2) {
throw new Error('Insufficient data. Expecting an array of at least 2 residuals.');
}
//...
}
Hydro
General
- Similarly to stats, use the destructuring parameter with default to an empty object. This makes each function available for usage within the HydroLang software. Example:
const functionName = ({params, args, data} = {}){/...}
- The functions are mostly implemented with real data coming from a sensor. Considering this, using array calculations rather than single values is more helpful.
- We need references for each of the functions/models you are implementing so that users can go back to the textbook or resource and further read. You can add it directly into the comments as follows:
/**
*...
*Reference: (link to book or reference)
*/
Penman Monteith
-Rename to ETPenmanMontheith
- Consider that temperature, netRadiation, windSpeed, and the vapor pressures can be fed as arrays. Change the function so that the evapotranspiration value is returned as a time series array rather than a single value.
- Make some simple validation to account for the inputs required. Generic example:
if (!temperature || !temperatureUnit || !netRadiation || !windSpeed || !saturationVaporPressure || !actualVaporPressure) {
throw new Error('Missing required parameters: temperature, temperatureUnit, netRadiation, windSpeed, saturationVaporPressure, actualVaporPressure.');
}
- Remove unnecessary 0 substraction in the denominator variable.
- Add reference to online resource or book section.
Hargraves Method
- Rename
ETHargreaves
- Move
latitude
toparams
. - Move
temperature
,tempmax
,tempmin
,date
to the data parameter. This should be fed as arrays. The code needs to be modified so it returns a time series. - Add some simple validation of the inputs, if the temperatures are not given throw an Error.
- The variable
julianDay
is not defined. Consider that the dates are in pair to the temperatures and coming from an API as a time string. A newgetJulianDate
helper function should be added to change the value from a date string to a julian value. Example:
function getJulianDay(date) {
let inputDate;
if (typeof date === 'string') {
inputDate = new Date(date);
} else if (date instanceof Date) {
inputDate = date;
} else {
throw new Error('Invalid date format. Expected a string or a Date object.');
}
const year = inputDate.getFullYear();
const month = inputDate.getMonth() + 1;
const day = inputDate.getDate();
// Julian Day Calculation
const a = Math.floor((14 - month) / 12);
const y = year + 4800 - a;
const m = month + 12 * a - 3;
return day + Math.floor((153 * m + 2) / 5) + 365 * y + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045;
}
- Update the documentation and put a reference.
Thornthwaite Method
- Rename
ETThornthwaite
temperature
andmonthdays
are data objects. This should be moved into thedata
argument.- Reference for the method on the comments.
- Data validation on temperature and monthDays.
Blaney-Criddle
- Rename
ETBlaneyCriddle
- Move
temperature
andmonthDays
todata
argument. - Put reference in comments.
- Implement validation to the data inputs.
Priestley-Taylor
- Rename
ETPriestelyTaylor
- Explain the inputs in the comments (netRadiation (units?), latentHeatFlux (units?))
- Put reference in comments.
Green-Ampt
- Rename
InfGreenAmpt
- Put reference in comments.
- Implement validation of data inputs.
Horton
- Rename
InfHorton
- Put reference in comments.
- Implement validation of data inputs.
Philip
- Rename
InfPhilip
- Put reference in comments.
- Implement validation of data inputs.
Muskingum-Cunge
- The implementation works good for small watersheds, but might not be memory efficient for large datasets. For a simple implementation, lets add a cache object to memoize and keep track of latter values and use them to propagate further. Example:
const getMemoInflow (K, inflow, i, cache) {
if (cache[index]) {
return cache[index];
}
const inflowComponent = K * inflow[index];
cache[index] = inflowComponent;
return inflowComponent;
}
const getMemoOuflow(K, X, inflow, prevStorage, inflowComponent, index, cache) {
if (cache[index]) {
return cache[index];
}
const outflowComponent = K * (inflow[index] + X * (inflowComponent - inflow[index]) + X * (prevStorage - inflowComponent));
cache[index] = outflowComponent;
return outflowComponent;
}
In the code:
static muskingumCunge({ params, args, data } ={}) {
//...
// Memoization cache
const cache = {};
for (let i = 0; i < inflow.length; i++) {
const prevStorage = storage;
const inflowComponent = getMemoInflow(K, inflow, i, cache);
const outflowComponent = getMemoOutflow(K, X, inflow, prevStorage, inflowComponent, i, cache);
//...
}
- Move
inflowData
todata
argument. - Explain the X and K parameters from the method.
- Put reference in comments.
lagRoute Method
- We can improve the storage output in a single loop rather than iterating through it over again. Example:
static lagAndRouter({params, args, data}){
//...
//Use the routing coefficients rather than the storage for update
for (let j = routingCoefficients.length - 1; j >= 1; j--) {
storage[j] = storage[j - 1] + routingCoefficients[j - 1] * inflow[i - j];
}
//...
}
- Put reference in comments.
- Explain lagTime and routing coefficients in comments.
Time-Area method
- Reference in comments.
- Units for both inflow and area data.
Kinematic Wave Routing
- Method only considers single channel routing. This should be explicitly mentioned in the comments.
- Move
initialDepth
toparams
orargs
- Explain the parameters used for the calculations attaching the variables defined within (C-travel time coefficient, etc)
- Put reference in comments.
Darcy's Law
- We can merge both cases into a single equation, passing a flag into the
params
to account for the changes. - Being aquifers dynamic systems, we can merge the three functions into one so there is no unnecessary repetition of work. Example:
static darcysLaw({params, args, data} = {}) {
const {aquiferType} = params;
const {hydraulicConductivity, porosity = 0} = args //add the porosity as an input variable. If porosity is not given (unconfined case), then it is set to 0 to avoid errors.
const {hydraulicGradients = [], aquiferThickness} = data; //1d arrays for each.
//...
const groundwaterFlows = [];
for (let i = 0; i < hydraulicGradients.length; i++) {
let groundwaterFlow;
if (aquiferType === 'confined') {
const transmissivity = hydraulicConductivity * aquiferThickness[i];
groundwaterFlow = transmissivity * hydraulicGradients[i];
} else if (aquiferType === 'unconfined') {
groundwaterFlow = hydraulicConductivity * hydraulicGradients[i] * aquiferThickness[i] * porosity;
}
groundwaterFlows.push(groundwaterFlow)
}
return groundwaterFlows;
- Put reference in comments.
- Explain the variables and their units in comments.
DescriptionThe following functions have been added into hydro.js: Type of change
How Has This Been Tested?Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Checklist:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Infiltration Philip
- There is a mistake with the name of the function.
TimeArea method
- Let's change the reference to the following: https://www.nohrsc.noaa.gov/technology/gis/uhg_manual.html. The one provided is too messy.
Proportional Distribution
- This function is good, but we can improve it by adding a more comprehensive overview of what would happen if we have a collection of basins that are close together and a rainfall event happens. We can then, by considering the distance between the basins and their areas rather than weights, distribute rainfall on a more likely scenario. We call this inverse distance weighting. If the distance parameter is not given, then it would do a proportional distribution as what you implemented. Example:
static inverseDistanceWeighting({ totalRainfall, basinAreas, distances } = {}) {
//Some error handling here
// If distances are not provided, distribute rainfall evenly across all basins
if (!distances) {
distances = new Array(basinAreas.length).fill(1); // Default all distances to 1 (even distribution)
}
//...
// Calculate the sum of the inverse distances
const sumInverseDistances = distances.reduce((acc, distance) => acc + 1 / distance, 0);
// Calculate the weights based on inverse distances
const weights = distances.map((distance) => (1 / distance) / sumInverseDistances);
// Calculate rainfall distribution based on weights and basin areas
const rainfallDistribution = basinAreas.map((area, index) => weights[index] * totalRainfall);
return rainfallDistribution
}
- Change the example.
Stochastic Rainfall
- We will modify this function so that a flag for the type of distribution that you apply should be given by the user, default to
normal
. This flag should be put in theparams
. - The data for the rainfall should be passed as an array directly as data: [dataArray].
mean
andstdDev
can be calculated using the functions instats
module.
CalibratePH
sensor_reading
will probably be given as an array. Move it to the data without passing it as a variable. Example:data: [sensorData]
.- You can pass the
slope
andintercept
as parameters directly into theparams
object. - Modify the function so that for each of the values passed in the
data
variable, the calibration happens.
DO Saturation
temperature
andsensor_reading
will probably be given as arrays. Consider them as such as pass them as such in to thedata
argument. Modify the function accordingly.
Compensate_EC
temperature
andsensor_reading
will probably be given as arrays. Consider them as such as pass them as such in to thedata
argument. Modify the function accordingly.
Calculate TDS
temperature
andsensor_reading
will probably be given as arrays. Consider them as such as pass them as such in to thedata
argument. Modify the function accordingly.
Compensate ORP
temperature
andsensor_reading
will probably be given as arrays. Consider them as such as pass them as such in to thedata
argument. Modify the function accordingly.
Generate Synthetic Values
- This function should change the random value based on the type of distribution passed to the
Stochasticgenerator
function. Call the available distributions fromstats
to be used in here. - There is already a normal distribution CDF available in
stats
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor mistakes that will be corrected during doc generation.
Description
The following functions have been added into stats.js:
Linear Moving Average
Differencing
Heterskedaticity
Multiply Matrix
Transpose Matrix
Matrix Inverse
Regression
Multi-Regression
Type of change
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Checklist: