import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mytoolbox import solar
Assignment 10
#10-02: Applying our module solar
- We want to look at the entire year 2023 in Dornbirn. Create a Pandas DataFrame indexed by the datetime of the year in hourly sampling.
- Use the functions of our solar module to compute new columns of the DataFrame: the hourangle, the declination, the solar elevation angle, the solar azimuth, and also the clear-sky irradiance. Use the latitude and longitude of Dornbirn as defined by the variables
LAT_DO
andLON_DO
insolar
. - When computing the DataFrame, I get a runtime warning that there were invalid values encountered in a function call to
arccos
. This means, we have to expect some missing values in our data set. Use the DataFrame methods.isna()
and.sum()
to check how many NaN’s our data set contains and which columns are affected.
In [1]:
In [3]:
# Generate a datetime series for all of 2023 with hourly sampling
= '2023-01-01'
start_date = '2023-12-31'
end_date = pd.date_range(start=start_date, end=end_date, freq='h')
datetime = pd.DataFrame(index=datetime) DF
In [4]:
DF
2023-01-01 00:00:00 |
2023-01-01 01:00:00 |
2023-01-01 02:00:00 |
2023-01-01 03:00:00 |
2023-01-01 04:00:00 |
... |
2023-12-30 20:00:00 |
2023-12-30 21:00:00 |
2023-12-30 22:00:00 |
2023-12-30 23:00:00 |
2023-12-31 00:00:00 |
8737 rows × 0 columns
In [5]:
'tau'] = solar.comp_hourangle(DF.index)
DF['declination'] = solar.comp_declination(DF.index.day_of_year)
DF['h'] = solar.comp_elevangle(DF['tau'], DF['declination'], solar.LAT_DO)
DF['alpha'] = solar.comp_solarazimuth(DF['h'], DF['tau'], DF['declination'], solar.LAT_DO)
DF['iswr_clearsky'] = solar.parameterize_iswr_clearsky(DF['h'], DF.index.day_of_year, solar.LAT_DO, DF['declination']) DF[
/home/flo/miniconda3/envs/scipro2024/lib/python3.11/site-packages/pandas/core/arraylike.py:399: RuntimeWarning: invalid value encountered in arccos
result = getattr(ufunc, method)(*inputs, **kwargs)
In [6]:
sum() DF.isna().
tau 0
declination 0
h 0
alpha 287
iswr_clearsky 0
dtype: int64
#10-03: Idealized conditions in Dornbirn
Let’s look a bit deeper into the idealized clear-sky irradiance in Dornbirn over 2023.
- Create quick working plots like the following ones without spending time to style them etc. You just want to look at the data as conveniently and quickly as possible.
In [7]:
= plt.subplots(ncols = 2, figsize = (10, 5))
fig, (ax1, ax2) 'iswr_clearsky'].plot(ax=ax1)
DF['iswr_clearsky'][0:49].plot(ax=ax2)
DF["iswr_clearsky_workingplots.png") plt.savefig(
- Compute the maximum and median clear-sky irradiance for Dornbirn each day in 2023. Similarly to your working plots above, apply the Pandas
.plot()
method (here,.plot.area()
) to create a plot that you then style a little bit with legend, title, ylabel. Make it look as closely as possible to that one:
Note that the circles representing monthly means are a bit of a tricky part. Here are a few tips:
- First resample to monthly sampling (using sampling frequency
'MS'
) and compute the average. - You will then get a Series with the desired values at the index values ‘start of each month’.
- To display the circles at the mid of each month of the graph, add a time offset of 14 days to the index of your resampled monthly mean Pandas Series.
In [8]:
= DF['iswr_clearsky'].resample("MS").mean()
monthly_mean = monthly_mean.index + pd.to_timedelta(14, unit="D")
monthly_mean.index
= plt.subplots()
fig, ax 'iswr_clearsky'].resample("D").max().plot.area(ax=ax, color="green", alpha=0.3, label="Daily maximum")
DF['iswr_clearsky'].resample("D").median().plot.area(ax=ax, color="green", alpha=0.5, label="Daily median")
DF[=ax, marker = "o", linestyle='none', color="yellow", label="Monthly mean")
monthly_mean.plot(ax"Idealized solar clear-sky irradiance in Dornbirn")
ax.set_title("Irradiance (W/m2)")
ax.set_ylabel(="upper right")
ax.legend(loc"iswr_clearsky_styled.png") plt.savefig(
#10-04: Histograms
We stick with the same DataFrame, but want to look at the data in a different way.
- We start simple and create some very quick working plots. A histogram of the solar elevation angle and one of the solar azimuth.
In [10]:
'h'].plot.hist() DF[
<Axes: ylabel='Frequency'>
In [11]:
'alpha'].plot.hist() DF[
<Axes: ylabel='Frequency'>
- Now that you have your working plots, let’s modify them a bit. Create three panels next to each other that share the same y-axis. Try to emulate the following plot as closely as possible.
In [14]:
# Figure and axes
= plt.subplots(ncols=3, sharey=True, figsize=(12,4))
fig, ax
# First panel
'h'] > 0, 'h'].plot.hist(ax=ax[0], color="orange", alpha=0.5, label="Day (h > 0)")
DF.loc[DF['h'] < 0, 'h'].plot.hist(ax=ax[0], color="gray", alpha=0.5, label="Night (h < 0)")
DF.loc[DF[
# Second panel
'h'] > 0, 'alpha'].plot.hist(ax=ax[1], color="orange", alpha=0.5, label="Day", bins=20)
DF.loc[DF['h'] < 0, 'alpha'].plot.hist(ax=ax[1], color="gray", alpha=0.5, label="Night", bins=16)
DF.loc[DF[
# Third panel
'alpha'] < 270) & (90 <= DF['alpha']), 'alpha'].plot.hist(ax=ax[2], color="dodgerblue", alpha=0.5, bins=12,
DF.loc[(DF[=r"North HS $(90 \leq \alpha < 270)$")
label270 <= DF['alpha']) | (DF['alpha'] < 90), 'alpha'].plot.hist(ax=ax[2], color="red", alpha=0.5, bins=22,
DF.loc[(=r"South HS $(270 \leq \alpha < 90)$")
label
# Styling and labeling
0].set_xlabel(r"Elevation angle h ($^\circ$)")
ax[0].legend()
ax[1].set_xlabel(r"Solar azimuth ($^\circ$)")
ax[1].legend()
ax[2].set_xlabel(r"Solar azimuth ($^\circ$)")
ax[2].legend()
ax["hist_elev_azimuth.png") plt.savefig(
#10-05: Box plot with multiple artists
This exercise will help you to refine your skills in reshaping data frames to create working plots with multiple artists efficiently.
- Extract the month information from the datetime index and write it to a new column named “month”.
- Create a wide data frame with month as columns and elevation angle h as values using the .pivot() method.
- Try to reproduce the following figure
In [15]:
'month'] = DF.index.month
DF[= DF.pivot(columns='month', values='h')
DF_wide
DF_wide.plot.box()
# The exact figure including the styles:
# fig, ax = plt.subplots(figsize=(6, 4))
# DF_wide.plot.box(ax=ax)
# ax.set_ylabel(r"Solar elevation angle ($^\circ$)")
# ax.set_xlabel("Month of year")
# ax.set_title("Variation in solar elevation angle over the entire year")
# plt.savefig("boxplot_series.png")
<Axes: >