HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Look up parameters based on a numpy array of input values

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
numpyarraylookinputbasedvaluesparameters

Problem

Aim: Write a function that looks up empirical parameters A, B and C for a solar radiation model. Each parameter has a value for a certain interval of time, represented as the day of the year. For now, I basically translate the four-column (day_of_year-interval, A, B, C) table that I have on paper into the following look up. The function works, but I have a feeling that this is not the most Pythonic way to do it.

Current implementation:

```
import numpy as np

def lookup_DNI_parameters(day_of_year):
"""Sets parameters A, B, and C for the calculation of direct normal irradiance
based on day of year-intervals.

A is in W/m^2
B and C are dimensionless
"""
A = np.empty_like(day_of_year)
B = np.empty_like(day_of_year)
C = np.empty_like(day_of_year)

# generate boolean indices for each day_of_year-interval
interval_1 = np.logical_and(day_of_year >=1, day_of_year = 32, day_of_year = 60, day_of_year = 91, day_of_year = 121, day_of_year = 152, day_of_year = 182, day_of_year = 213, day_of_year = 244, day_of_year = 274, day_of_year = 305, day_of_year = 335

# set parameter values based on the interval
A[interval_1] = 1230.0
A[interval_2] = 1215.0
A[interval_3] = 1186.0
A[interval_4] = 1136.0
A[interval_5] = 1104.0
A[interval_6] = 1088.0
A[interval_7] = 1085.0
A[interval_8] = 1107.0
A[interval_9] = 1152.0
A[interval_10] = 1193.0
A[interval_11] = 1221.0
A[interval_12] = 1234.0

B[interval_1] = 0.142
B[interval_2] = 0.144
B[interval_3] = 0.156
B[interval_4] = 0.180
B[interval_5] = 0.196
B[interval_6] = 0.205
B[interval_7] = 0.207
B[interval_8] = 0.201
B[interval_9] = 0.177
B[interval_10] = 0.160
B[interval_11] = 0.149
B[interval_12] = 0.142

C[interval_1] = 0.058
C[interval_2] = 0.060
C[interval_3] = 0.071
C[interval_4] = 0.097
C[interval_5] = 0.121
C[interval_6] = 0.134
C[interval_7] = 0.136
C[

Solution

So you are doing a lookup over many intervals. A repeated pattern like that can be turned into a loop or vectorized

The repeated boilerplate suggests an iterative solution - looping over an array (or list) of dates and values. It doesn't save on run time, and may, or may not, be more maintainable.

def lookup_DNI_parameters1(day_of_year):
    """solution based on iterating over ranges of dates
    """
    A = np.empty_like(day_of_year)
    dates = [1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
    Avalues = [1230.0, 1215.0, 1186.0, 1136.0, 1104.0, 1088.0, 1085.0, 1107.0, 1152.0, 1193.0, 1221.0, 1234.0]
    for i in range(len(dates)-1):
        interval = np.logical_and(day_of_year >= dates[i], day_of_year < dates[i+1])
        A[interval] = Avalues[i]
    # similarly for B and C
    return A


The operation can also be vectorized (in MATLAB as well as numpy), by constructing a matrix that compares the whole day_of_year array against the dates intervals array(s). The [:,None] broadcasting may be novel to a MATLAB user.

def lookup_DNI_parameters2(day_of_year):
    """solution based on vectorized matching of days
    """
    dates = [1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
    dates1 = np.array(dates[:-1]) # interval start
    dates2 = np.array(dates[1:]) # interval stop
    Avalues = [1230.0, 1215.0, 1186.0, 1136.0, 1104.0, 1088.0, 1085.0, 1107.0, 1152.0, 1193.0, 1221.0, 1234.0]
    Avalues = np.array(Avalues)
    I = (dates1[:,None] <= day_of_year) & (day_of_year < dates2[:,None]) # boolean index array
    I = np.where(I)[0]  # convert to numeric index
    return Avalues[I]


Or this could be generalized to use a dates parameter, and a values array that has information for A, B and C:

def lookup_DNI_parameters3(day_of_year, dates, values):
    """solution based on vectorized matching of days
    """
    dates1 = np.array(dates[:-1]) # interval start
    dates2 = np.array(dates[1:]) # interval stop
    I = (dates1[:,None] <= day_of_year) & (day_of_year < dates2[:,None]) # boolean index array
    I = np.where(I)[0]  # convert to numeric index
    return values[...,I]

dates = [1, 32, 60, 91,...]
values = np.array([[1230.0, 1215.0,...], [142., 144., ...], [...]])
lookup_DNI_parameters3(day_of_years, dates, values)

Code Snippets

def lookup_DNI_parameters1(day_of_year):
    """solution based on iterating over ranges of dates
    """
    A = np.empty_like(day_of_year)
    dates = [1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
    Avalues = [1230.0, 1215.0, 1186.0, 1136.0, 1104.0, 1088.0, 1085.0, 1107.0, 1152.0, 1193.0, 1221.0, 1234.0]
    for i in range(len(dates)-1):
        interval = np.logical_and(day_of_year >= dates[i], day_of_year < dates[i+1])
        A[interval] = Avalues[i]
    # similarly for B and C
    return A
def lookup_DNI_parameters2(day_of_year):
    """solution based on vectorized matching of days
    """
    dates = [1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
    dates1 = np.array(dates[:-1]) # interval start
    dates2 = np.array(dates[1:]) # interval stop
    Avalues = [1230.0, 1215.0, 1186.0, 1136.0, 1104.0, 1088.0, 1085.0, 1107.0, 1152.0, 1193.0, 1221.0, 1234.0]
    Avalues = np.array(Avalues)
    I = (dates1[:,None] <= day_of_year) & (day_of_year < dates2[:,None]) # boolean index array
    I = np.where(I)[0]  # convert to numeric index
    return Avalues[I]
def lookup_DNI_parameters3(day_of_year, dates, values):
    """solution based on vectorized matching of days
    """
    dates1 = np.array(dates[:-1]) # interval start
    dates2 = np.array(dates[1:]) # interval stop
    I = (dates1[:,None] <= day_of_year) & (day_of_year < dates2[:,None]) # boolean index array
    I = np.where(I)[0]  # convert to numeric index
    return values[...,I]

dates = [1, 32, 60, 91,...]
values = np.array([[1230.0, 1215.0,...], [142., 144., ...], [...]])
lookup_DNI_parameters3(day_of_years, dates, values)

Context

StackExchange Code Review Q#72258, answer score: 3

Revisions (0)

No revisions yet.