'Dividing values in columns based on their previous marker

I have the following dataframe:

df = {'id': [1,2,3,4],
    '1': ['Green', 'Green', 'Green', 'Green'],
  '2': ['34','67', 'Blue', '77'],
  '3': ['Blue', '45', '99', 'Blue'],
  '4': ['12', None, '101', '56'],
     '5': [None, None, None, '23']}

df = pd.DataFrame(df)


    id  1       2     3     4       5
0   1   Green   34    Blue  12      None
1   2   Green   67    45    None    None
2   3   Green   Blue  99    101     None
3   4   Green   77    Blue  56      23

I would like to creating separate dataframes for GREEN and BLUE with their id number. For example the output of the GREEN DF would be:

  id   number
   1     34
   2     67
   2     45
   4     77


Solution 1:[1]

You can try:

# set id as index
df = df.set_index('id')

# mask the color
mask = df.isin(['Green','Blue'])

# label the cells' color with `where` and `ffill`
# print `df.where(mask).ffill(axis=1)` if you want to know how it looks
out = pd.DataFrame({'color':df.where(mask).ffill(axis=1).stack(),
                    'value':df.mask(mask).stack()
                  }).dropna()

# blue color
out.query('color=="Blue"')

Output:

     color value
id              
1  4  Blue    12
3  3  Blue    99
   4  Blue   101
4  4  Blue    56
   5  Blue    23

Note first level index is the old column, you can drop it if you don't want to.

Solution 2:[2]

Check

df.set_index('id',inplace=True)

out = df.where(df.where(lambda x : x.isin(['Green','Blue'])).ffill(axis=1).eq('Green')).stack().loc[lambda x : x!='Green']
Out[571]: 
id   
1   2    34
2   2    67
    3    45
4   2    77
dtype: object

Solution 3:[3]

Here is one approach, by stacking first to Series:

# set id as index stack to Series (removing the NaNs)
s = df.set_index('id').stack()

# which values are not Green/Blue?
m = ~s.isin(['Green', 'Blue'])

# ffill and select the non Green/Blue positions
df2 = pd.DataFrame({'color': s.mask(m).ffill().loc[m],
                    'number': s.loc[m]},
                   ).droplevel(1) # keep level if you want the column labels

output:

    color number
id              
1   Green     34
1    Blue     12
2   Green     67
2   Green     45
3    Blue     99
3    Blue    101
4   Green     77
4    Blue     56
4    Blue     23

Solution 4:[4]

I'd create a dictionary of dataframes with the key of colors like this:

dfm =  df.melt('id').sort_values(['id', 'variable'])[['value']]

dfm['group'] = dfm.where(dfm.isin(['Green', 'Blue'])).ffill()['value']

c_dict = dict(tuple(dfm.dropna(how='any').query('value != group').groupby('group')[['value']]))

Output:

#c_dict['Blue']
   value
12    12
10    99
14   101
15    56
19    23

and

#c_dict['Green']
  value
4    34
5    67
9    45
7    77

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Quang Hoang
Solution 2 BENY
Solution 3 mozway
Solution 4 Scott Boston