I have the following multi-indexed DataFrame.
import pandas as pd
import numpy as np
# Create df ---
names = ['add', 'sub', 'mul', 'divi'] # List of the names of our maths functions
size = 4
tups = [('scalars', 'add', 'sc0'), ('scalars', 'add', 'sc1'), ('scalars', 'add', 'sc2'), ('scalars', 'sub', 'sc0'), ('scalars', 'sub', 'sc1'), ('scalars', 'mul', 'sc0'), ('scalars', 'mul', 'sc1'), ('scalars', 'mul', 'sc2'), ('scalars', 'divi', 'sc0'), ('scalars', 'divi', 'sc1'), ('operator', '', '')]
df = pd.DataFrame(columns=pd.MultiIndex.from_tuples(tups))
df['operatorIndex'] = np.random.randint(0, len(names), size)
df['operator'] = df['operatorIndex'].apply(lambda x: str(names[x]))
groupSize = 2
df['ID']=np.divmod(np.arange(len(df)),groupSize)[0] 1
df.set_index('ID', inplace=True)
df.sort_index(inplace=True)
> df
scalars operator operatorIndex
add sub mul divi
sc0 sc1 sc2 sc0 sc1 sc0 sc1 sc2 sc0 sc1
ID
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN mul 2
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN add 0
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN sub 1
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN mul 2
How could I assign random integers (between 0 and 100) only to sc
columns that correlate to the value in each row's operator
column? Preferably, this would be done without any loops.
An example could look like
> df
scalars operator operatorIndex
add sub mul divi
sc0 sc1 sc2 sc0 sc1 sc0 sc1 sc2 sc0 sc1
ID
1 NaN NaN NaN NaN NaN 7 3 9 NaN NaN mul 2
1 29 12 11 NaN NaN NaN NaN NaN NaN NaN add 0
2 NaN NaN NaN 6 36 NaN NaN NaN NaN NaN sub 1
2 NaN NaN NaN NaN NaN 16 2 29 NaN NaN mul 2
CodePudding user response:
First, mask the values where the 2nd column level (df.columns.get_level_values(level=1)
) coincides with the operator column value (df['operator']
). This can be done by converting both to numpy arrays (using the to_numpy
method) and taking advantage of numpy broadcasting.
mask = df.columns.get_level_values(level=1).to_numpy() == df['operator'].to_numpy()[:, None]
Then pass this mask to DataFrame.mask
, and update the locations of True values with random integers between 0 and 100.
res = df.mask(mask, np.random.randint(0, 101, size=df.shape))
Output
# Given the input df
>>> df
scalars operator operatorIndex
add sub mul divi
sc0 sc1 sc2 sc0 sc1 sc0 sc1 sc2 sc0 sc1
ID
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN mul 2
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN add 0
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN sub 1
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN mul 2
>>> mask
[[False False False False False True True True False False False False]
[ True True True False False False False False False False False False]
[False False False True True False False False False False False False]
[False False False False False True True True False False False False]]
>>> res
add sub mul divi
sc0 sc1 sc2 sc0 sc1 sc0 sc1 sc2 sc0 sc1
ID
1 NaN NaN NaN NaN NaN 2 79 17 NaN NaN mul 2
1 15 16 30 NaN NaN NaN NaN NaN NaN NaN add 0
2 NaN NaN NaN 35 94 NaN NaN NaN NaN NaN sub 1
2 NaN NaN NaN NaN NaN 11 95 48 NaN NaN mul 2