As the world tries to stop planet warming emissions, solar and wind power have taken central stage. One of the reason solar and wind go well together is their complementary seasonal pattern. The magnitude of this pattern depends on the location but in Europe winter means less solar power but more wind power. Summer on the other hand means more solar but slightly less wind. So if you want to have renewable energy all year round, it’s a good idea to build both solar and wind capacity.
On https://energy-charts.info/ raw data has recently become available for download and I have been doing data visualization to show the complementary seasonal pattern with actual data from several countries. Here I show some examples and the code. The code I am showing can be adapted for different countries. Here is the code and an example figure.
import os
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
dirname = os.path.dirname(__file__)
data_path = os.path.join(dirname, 'data')
country = 'Deutschland'
raw_files = [os.path.join(data_path, f)
for f in os.listdir(data_path) if country in f]
loaded_files = [pd.read_csv(f) for f in raw_files]
df = pd.concat(loaded_files, ignore_index=True)
df['Datum (UTC)'] = pd.to_datetime(df['Datum (UTC)'])
df['day_of_year'] = df['Datum (UTC)'].dt.dayofyear
df['year'] = df['Datum (UTC)'].dt.year
df['month'] = df['Datum (UTC)'].dt.month
df['day'] = df['Datum (UTC)'].dt.day
df['Wind'] = df['Wind Offshore'] + df['Wind Onshore']
# plt.figure()
# plt.hist(df.loc[df['year'] == 2021]['Wind+Solar'], bins=100)
# Calculate year as theta on the cycle
# df['theta_seconds'] = np.nan
# Plot solar development
df_daily = df.groupby(by=['year', 'month', 'day']).mean()
df_daily = df_daily.reset_index()
# Calculate daily theta
for year in df['year'].unique():
boolean_year = df_daily['year'] == year
days_zero = (df_daily[boolean_year]['day_of_year']
- df_daily[boolean_year]['day_of_year'].min())
theta = days_zero / days_zero.max()
df_daily.loc[boolean_year, 'theta_days'] = theta * 2 * np.pi
font = {'size' : 16}
matplotlib.rc('font', **font)
solar_colors = ['#fef0d9','#fdd49e','#fdbb84','#fc8d59',
'#ef6548','#d7301f','#990000']
fig, ax = plt.subplots(1, 2, subplot_kw={'projection': 'polar'})
for idx, y in enumerate(df_daily['year'].unique()):
r = df_daily[df_daily['year'] == y]["Solar"] / 1000 # MW to GW
theta = df_daily[df_daily['year'] == y]['theta_days']
ax[0].plot(theta, r, alpha=0.8, color=solar_colors[idx])
r = df_daily[df_daily['year'] == y]["Wind"] / 1000 # MW to GW
ax[1].plot(theta, r, alpha=0.8, color=solar_colors[idx])
# Find month starting theta
month_thetas = []
for i in range(1,13):
idx = (df_daily['month'] == i).idxmax()
month_thetas.append(df_daily['theta_days'][idx])
month_labels = ['January', 'February', 'March', 'April', 'May',
'June', 'July', 'August', 'September', 'October',
'November', 'December']
for i in [0,1]:
tl = np.array(month_thetas) / (2*np.pi) * 360
ax[i].set_thetagrids(tl, month_labels)
ax[i].xaxis.set_tick_params(pad=28)
ax[i].set_theta_direction(-1)
ax[0].set_rticks([5, 7.5, 10, 12.5])
ax[1].set_rticks([20, 30, 40])
ax[0].set_rlabel_position(-10) # Move radial labels
ax[1].set_rlabel_position(-170)
ax[1].legend(df_daily['year'].unique(), bbox_to_anchor=(1.1, 1.2))
fig.suptitle("Average daily power (in GW) from solar (left) and" +
"wind (right) in Germany for years 2015-2021.\n" +
"Data from energy-charts.info. Plot by Daniel" +
" Müller-Komorowska.")

The code itself is pretty simple because the data already comes well structured. Every year is in an individual file so we need to find all of those in the data directory. Probably the biggest preprocessing step is to calculate the daily average. The original data has a temporal resolution of 15 minutes which is a bit too noisy for the type of plot we are making. Once we extracted year, month and day we can do it on one line:
df_daily = df.groupby(by=['year', 'month', 'day']).mean().reset_index()
Next, we calculate theta for each day so we can distribute the datapoints along the polar plot. Once we have that we are basically done. The rest is matplotlib styling. Some of these styling aspects are hardcoded because they are hard to automate for different countries. Here are two more examples:


This is a lovely use of data. I’ll be showing the blog page to some of my Python students soon. But I was hoping to be able to run your code for the data from the US. The code appears to run fine in my dev environment but I’m not finding raw data downloads on https://energy-charts.info/. Does one need to be a subscriber to their service to get access?
LikeLike
I am happy you like the data visualization and I hope it will be useful to your students. I am not affiliated with Energy Charts but as far as I know, it is an initiative by Prof Dr. Bruno Burger at the Frauenhofer Institute in Germany. They take data from grid operators and make data dashboards with it. As far as possible they make the data underlying their dashboards available for download. In the top right of their website are all the countries they have data available on and unfortunately I don’t see US data there. With a few tweaks the data visualization could be done with another data source that has at least daily generation data.
LikeLike