pythonnumpypandas
Ben Gorman

Ben Gorman

Life's a garden. Dig it.

Given, two Series bees and knees, if the ith value of bees is NaN, double the ith value inside knees.

import numpy as np
import pandas as pd
 
bees = pd.Series([True, True, False, np.nan, True, False, True, np.nan])
knees = pd.Series([5,2,9,1,3,10,5,2], index = [7,0,2,6,3,5,1,4])
 
print(bees)
# 0     True
# 1     True
# 2    False
# {==3      NaN==}
# 4     True
# 5    False
# 6     True
# {==7      NaN==}
# dtype: object
 
print(knees)
# 7     5
# 0     2
# 2     9
# {==6     1==}  <-- double this
# 3     3
# 5    10
# 1     5
# {==4     2==}  <-- double this
# dtype: int64

Expected Result

print(knees)
# 7     5
# 0     2
# 2     9
# {==6     2==}
# 3     3
# 5    10
# 1     5
# {==4     4==}
# dtype: int64

Solution

knees.loc[pd.isna(bees).to_numpy()] *= 2
 
print(knees)
# 7     5
# 0     2
# 2     9
# {==6     2==}
# 3     3
# 5    10
# 1     5
# {==4     4==}
# dtype: int64

Explanation

  1. Identify which elements of bees are NaN with pd.isna().

    pd.isna(bees)
    # 0    False
    # 1    False
    # 2    False
    # {==3     True==}
    # 4    False
    # 5    False
    # 6    False
    # {==7     True==}
    # dtype: bool

    This results in a boolean Series.

  2. Suppose we use this boolean Series to index knees..

    knees.loc[pd.isna(bees)]
    # 7    5
    # 3    3
    # dtype: int64

    The elements it selects are not the ones we want.

    knees.loc[pd.isna(bees)] selects the elements of knees whose index matches those of pd.isna(bees) with True values.

    knees.loc[pd.isna(bees)]
     
    # pd.isna(bees)        knees
    # 0      False  ┌------{==7-----5==}
    # 1      False  |      0     2
    # 2      False  |      2     9
    # {==3-------True==}--┼--┐   6     1
    # 4      False  |  └---{==3-----3==}
    # 5      False  |      5    10
    # 6      False  |      1     5
    # {==7-------True==}--┘      4     2

    In other words, pd.isna(bees) has True values at indexes 3 and 7, so knees.loc[pd.isna(bees)] selects rows of knees with index 3 and 7.

    However, the problem asked us to double the ith value of knees if the ith value of bees was NaN. The fourth and eighth elements of bees are NaN, so we want to select the fourth and eight elements of knees. (Note that knees's index is not in sequential order.)

    To prevent Pandas from using the index of pd.isna(bees) to select elements of knees, we can remove the index by converting the boolean Series to a NumPy array. Then Pandas will select elements by the position of True values.

    knees.loc[pd.isna(bees).to_numpy()]
     
    # pd.isna(bees).to_numpy()]     knees
    # False                         7     5
    # False                         0     2
    # False                         2     9
    #  {==True==}-------------------------{==6-----1==}
    # False                         3     3
    # False                         5    10
    # False                         1     5
    #  {==True==}-------------------------{==4-----2==}
     
    # <result>
    # 6    1
    # 4    2
    # dtype: int64
  3. Double the value of those elements.

    Easy enough, thanks to the += operator :fontawesome-regular-face-smile:

    knees.loc[pd.isna(bees).to_numpy()] *= 2
     
    print(knees)
    # 7     5
    # 0     2
    # 2     9
    # {==6     2==}
    # 3     3
    # 5    10
    # 1     5
    # {==4     4==}
    # dtype: int64