Home > Software design >  Clean way to return default when taking minimum of empty NumPy array
Clean way to return default when taking minimum of empty NumPy array

Time:12-17

I have two arrays, one holding a series of years and another holding some quantities. I want to study for each year how long it takes for the quantity to double.

For this I wrote this code:

years = np.arange(2020, 2060)
qma = np.array([8.00000000e 13, 8.14928049e 13, 8.30370113e 13, 8.46353044e 13,
       8.62905581e 13, 8.80058517e 13, 8.97844887e 13, 9.16300175e 13,
       9.35462542e 13, 9.55373083e 13, 9.76076116e 13, 9.97619497e 13,
       1.02005499e 14, 1.04343864e 14, 1.06783128e 14, 1.09329900e 14,
       1.11991375e 14, 1.14775397e 14, 1.17690539e 14, 1.20746183e 14,
       1.23952624e 14, 1.27321176e 14, 1.30864305e 14, 1.34595778e 14,
       1.38530838e 14, 1.74048570e 14, 1.92205500e 14, 2.14405932e 14,
       2.42128686e 14, 2.77655470e 14, 3.24688168e 14, 3.89624819e 14,
       4.84468500e 14, 6.34373436e 14, 9.74364148e 14, 2.33901669e 15,
       1.78934647e 16, 4.85081278e 20, 8.63469750e 21, 2.08204297e 22])

def doubling_year(idx):
      try:
        return years[qma >= 2*qma[idx]].min()
      except ValueError:
        return np.nan
    
years_until_doubling = [doubling_year(idx) - years[idx] 
                        for idx in range(len(years))]

This works as I expect, but having to define a named function for what is essentially a one-liner feels wrong. Is there a cleaner and more succing way of replicating this behaviour?

CodePudding user response:

Seems like we could take final year out of the loop and instead of minimizing index of True, count the number of Falses in doubling_year:

years_until_doubling = years[-1]   1 - np.array([(qma >= 2*q).sum() y for q, y in zip(qma, years)])

CodePudding user response:

For each year in the series, the number of years in which the quantity is double or more the original quantity can be computed through broadcasting. Then you simply have to subtract that number from the number of years remaining in the series and replace the 0's with np.nan's.

In [426]: n_doubled = np.sum(qma[None, :] >= 2*qma[:, None], axis=1)

In [427]: n_doubled
Out[427]: 
array([15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 12, 12,
       12, 12, 12, 11, 11, 11, 11, 11,  9,  9,  8,  8,  7,  6,  6,  6,  5,
        5,  4,  3,  2,  1,  0])

In [428]: np.where(n_doubled, np.arange(len(years), 0, -1) - n_doubled, np.nan)
Out[428]: 
array([25., 24., 23., 22., 21., 21., 20., 19., 18., 17., 17., 16., 15.,
       14., 13., 13., 12., 11., 10.,  9.,  9.,  8.,  7.,  6.,  5.,  6.,
        5.,  5.,  4.,  4.,  4.,  3.,  2.,  2.,  1.,  1.,  1.,  1.,  1.,
       nan])
  • Related