Skip to content

Instantly share code, notes, and snippets.

@ismasan
Created February 27, 2026 15:45
Show Gist options
  • Select an option

  • Save ismasan/49e0d3f41b13282541bc1c27e7f364a3 to your computer and use it in GitHub Desktop.

Select an option

Save ismasan/49e0d3f41b13282541bc1c27e7f364a3 to your computer and use it in GitHub Desktop.
module Energy
class Comparison
module LoadProfileGapFiller
module_function
# Public API
def fill_gaps(annual_coefficients:, customer_profile:)
# 1. Compute observed totals
observed_total = customer_profile.values.sum
raise "Customer profile is empty" if observed_total.zero?
# 2. Aggregate national + customer to monthly totals
national_monthly = monthly_totals(annual_coefficients)
customer_monthly = monthly_totals(customer_profile)
# 3. Compute monthly bias factors
monthly_bias = compute_monthly_bias(
national_monthly: national_monthly,
customer_monthly: customer_monthly,
observed_total: observed_total
)
# 4. Build adjusted coefficient curve
adjusted_coefficients = apply_monthly_bias(
annual: annual_coefficients,
monthly_bias: monthly_bias
)
# 5. Renormalise adjusted coefficients to sum to 1
renormalised = renormalise(adjusted_coefficients)
# 6. Estimate missing total energy
missing_keys = annual_coefficients.keys - customer_profile.keys
missing_fraction = missing_keys.sum { |ts| renormalised[ts] }
estimated_annual_total = observed_total / (1 - missing_fraction)
# 7. Fill gaps
filled = annual_coefficients.each_with_object({}) do |(ts, _), h|
if customer_profile.key?(ts)
h[ts] = customer_profile[ts]
else
h[ts] = estimated_annual_total * renormalised[ts]
end
end
filled
end
def monthly_totals(series)
series.each_with_object(Hash.new(0.0)) do |(ts, value), h|
key = [ts.year, ts.month]
h[key] += value
end
end
def compute_monthly_bias(national_monthly:, customer_monthly:, observed_total:)
national_observed_monthly = national_monthly.select do |(year, month), _|
customer_monthly.key?([year, month])
end
national_observed_total = national_observed_monthly.values.sum
bias = {}
national_monthly.each do |(year, month), national_month_total|
if customer_monthly.key?([year, month])
customer_fraction =
customer_monthly[[year, month]] / observed_total
national_fraction =
national_month_total / national_observed_total
raw_bias = customer_fraction / national_fraction
# Clamp to avoid extreme distortion
bias[[year, month]] = clamp(raw_bias, 0.5, 2.0)
else
bias[[year, month]] = 1.0
end
end
bias
end
def apply_monthly_bias(annual:, monthly_bias:)
annual.transform_values.with_index do |value, idx|
ts = annual.keys[idx]
factor = monthly_bias[[ts.year, ts.month]] || 1.0
value * factor
end
end
def renormalise(series)
total = series.values.sum
series.transform_values { |v| v / total }
end
def clamp(value, min, max)
[[value, min].max, max].min
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment