Last active
January 20, 2023 15:18
-
-
Save pakdev/2faf62302c831e8f180dc458d4266818 to your computer and use it in GitHub Desktop.
Calculates projected yearly power costs for all the providers on powertochoose.org using historical usage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
from collections import namedtuple | |
from datetime import datetime | |
from operator import attrgetter | |
import numpy | |
import requests | |
Option = namedtuple('Option', 'plan price') | |
def get_plans(include_tiered): | |
url = 'http://powertochoose.org/en-us/service/v1/' | |
r = requests.post(url, json={ | |
'method': 'plans', | |
'zip_code': '78728', | |
'estimated_use': 1000, | |
'include_details': True, | |
'min_usage_plan': '' if include_tiered else 'off', | |
'plan_type': '1,2' # 1: Fixed, 0: Variable, 2: Indexed | |
}) | |
return r.json() | |
def get_options(plans, tiered): | |
options = [] | |
incomplete_options = [] | |
for plan in plans: | |
x = [500, 1000, 2000] | |
y = [ | |
plan['price_kwh500'], | |
plan['price_kwh1000'], | |
plan['price_kwh2000'] | |
] | |
plan['tiered'] = tiered | |
term_length = int(plan['term_value']) | |
if term_length == 0: | |
incomplete_options.append(Option(plan=plan, price='???')) | |
continue | |
cur_month = datetime.now().month - 1 # Make January start at 0 | |
months_in_term = [mo % 12 for mo in range( | |
cur_month, term_length + cur_month)] | |
term_price = 0 | |
if not tiered: | |
# Note: 2nd order polynomial will always fit 3 points exactly | |
best_fit = numpy.poly1d(numpy.polyfit(x, y, 2)) | |
for month in months_in_term: | |
usage = my_usage[month] | |
month_price = (best_fit(usage) / 100.0) * usage | |
term_price += month_price | |
else: | |
for month in months_in_term: | |
usage = my_usage[month] | |
if usage >= 500 and usage < 1000: | |
month_price = usage * y[0] / 100.0 | |
elif usage >= 1000 and usage < 1500: | |
month_price = usage * y[1] / 100.0 | |
elif usage >= 1500 and usage < 2000: | |
month_price = usage * y[2] / 100.0 | |
elif usage < 500: | |
# TODO: improve? | |
month_price = usage * y[0] / 100.0 + 5 # Assume $5 fee | |
else: | |
# TODO: improve? | |
month_price = usage * y[2] / 100.0 | |
term_price += month_price | |
# Price is the average price we'll pay for this plan over its term months | |
options.append(Option(plan=plan, price=term_price / term_length)) | |
return options + incomplete_options | |
def print_options(options): | |
def get_plan_row(plan): | |
return str.join(',', map(lambda x: str(x), [ | |
plan['company_name'], | |
plan['plan_name'], | |
plan['term_value'], | |
plan['price_kwh500'], | |
plan['price_kwh1000'], | |
plan['price_kwh2000'], | |
plan['renewable_energy_id'], | |
plan['tiered'] | |
])) | |
# Sort by price ascending | |
sorted_options = sorted(options, key=attrgetter('price')) | |
print('Avg Price per Month,Company,Plan,Months,500 Price,1000 Price,1500 Price,% Renewable,Tiered') | |
for option in sorted_options: | |
row = str.join(',', [f"{option.price:.2f}", get_plan_row(option.plan)]) | |
print(row) | |
if __name__ == '__main__': | |
# 2 years | |
my_usage = [ | |
754, | |
687, | |
724, | |
942, | |
1489, | |
1247, | |
1666, | |
1414, | |
1011, | |
657, | |
643, | |
664, | |
718, | |
555, | |
674, | |
761, | |
813, | |
1494, | |
1845, | |
1991, | |
1700, | |
1009, | |
645, | |
623 | |
] | |
if len(my_usage) % 12 != 0: | |
raise Exception('Usage data must be multiples of 12 (months)') | |
non_tiered_plans = get_plans(False) | |
non_tiered_plan_ids = [x['plan_id'] for x in non_tiered_plans] | |
all_plans = get_plans(True) | |
tiered_plans = [x for x in all_plans if x['plan_id'] | |
not in non_tiered_plan_ids] | |
optionsA = get_options(non_tiered_plans, False) | |
optionsB = get_options(tiered_plans, True) | |
print_options(optionsA + optionsB) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment