Fix: Interpolate missing ENTSOE price data points
Handle missing hourly price data from ENTSOE API by interpolating values from adjacent time slots instead of returning nil.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
.config
|
||||
.project
|
||||
*.pid
|
||||
data
|
||||
|
||||
@@ -104,14 +104,60 @@ class Entsoe
|
||||
|
||||
doc = Nokogiri::XML(URI.open(url))
|
||||
|
||||
#p "Entsoe prices: %s" % doc
|
||||
|
||||
prices = doc.xpath('.//xmlns:Point').map{|p| parse_point(p)}
|
||||
begin
|
||||
# get start_time (in UTC) from XML docment
|
||||
start_time = DateTime.parse(doc.xpath('.//xmlns:period.timeInterval//xmlns:start').text)
|
||||
|
||||
# Get resolution to determine expected number of positions
|
||||
resolution = doc.xpath('.//xmlns:resolution').text
|
||||
interval_minutes = resolution == 'PT15M' ? 15 : 60
|
||||
expected_positions = (24 * 60) / interval_minutes
|
||||
|
||||
# Create hash from available prices
|
||||
price_hash = prices.to_h
|
||||
|
||||
# Fill in missing positions by interpolating from adjacent values
|
||||
complete_prices = {}
|
||||
(1..expected_positions).each do |position|
|
||||
if price_hash.key?(position)
|
||||
complete_prices[position] = price_hash[position]
|
||||
else
|
||||
# Find previous and next available prices for interpolation
|
||||
prev_price = (position-1).downto(1).find { |p| price_hash.key?(p) }
|
||||
next_price = (position+1).upto(expected_positions).find { |p| price_hash.key?(p) }
|
||||
|
||||
if prev_price && next_price
|
||||
# Interpolate between previous and next
|
||||
complete_prices[position] = ((price_hash[prev_price] + price_hash[next_price]) / 2.0).round(5)
|
||||
elsif prev_price
|
||||
# Use previous price as fallback
|
||||
complete_prices[position] = price_hash[prev_price]
|
||||
elsif next_price
|
||||
# Use next price as fallback
|
||||
complete_prices[position] = price_hash[next_price]
|
||||
end
|
||||
|
||||
# Calculate the formatted hour for the warning message
|
||||
hour_offset = interval_minutes == 15 ? (position - 1) / 4 : (position - 1)
|
||||
missing_hour = start_time.advance(hours: hour_offset).in_time_zone(zone).strftime("%F %H")
|
||||
p "Warning: Missing Entsoe data for #{missing_hour}, interpolated value: #{complete_prices[position]}"
|
||||
end
|
||||
end
|
||||
|
||||
#returns a hash with keys formatted "yyyy-mm-dd hr" and values price (per kwh)
|
||||
# <position> tag runs from 1-24. We need hours from 00-23, therefore substracting 1
|
||||
prices.map{|p| [start_time.advance(hours: (p[0]-1)), p[1]]}.to_h
|
||||
# <position> tag runs from 1-96 for 15min intervals. Convert to hourly by taking first interval of each hour
|
||||
if interval_minutes == 15
|
||||
# For 15-minute intervals, use the first interval of each hour (positions 1, 5, 9, 13, ...)
|
||||
complete_prices.select { |pos, _| (pos - 1) % 4 == 0 }
|
||||
.map { |pos, price| [start_time.advance(hours: ((pos - 1) / 4)), price] }
|
||||
.to_h
|
||||
else
|
||||
# For hourly data (position runs from 1-24, we need hours from 00-23)
|
||||
complete_prices.map { |pos, price| [start_time.advance(hours: (pos - 1)), price] }.to_h
|
||||
end
|
||||
rescue Date::Error => e
|
||||
p e.message
|
||||
{}
|
||||
|
||||
Reference in New Issue
Block a user