Get column name where value match with multiple condition python

Question:

Looking for a solution to my problem an entire day and cannot find the answer. I’m trying to follow the example of this topic: Get column name where value is something in pandas dataframe
to make a version with multiple conditions.

I want to extract column name (under a list) where :

value == 4 or/and value == 3
+
Only if there is no 4 or/and 3, then extract the column name where value == 2

Example:

data = {'Name': ['Tom', 'Joseph', 'Krish', 'John'], 'acne': [1, 4, 1, 2], 'wrinkles': [1, 3, 4, 4],'darkspot': [2, 2, 3, 4] }  

df1 = pd.DataFrame(data)  
df1

df1
”’

    Name    acne    wrinkles    darkspot
0   Tom     1         1            2   
1   Joseph  4         3            2
2   Krish   1         4            3
3   John    2         4            4

”’

The result i’m looking for :

df2

    Name    acne    wrinkles    darkspot   problem
0   Tom     1         1            2       [darkspot]
1   Joseph  4         3            2       [acne, wrinkles]
2   Krish   1         4            3       [wrinkles, darkspot]
3   John    2         4            4       [wrinkles, darkspot]

”’

I tried with the apply function with a lambda detailled in the topic i mentionned above but it can only take one argument.
Many thanks for your answers if somebody can help me 🙂

Asked By: Gabriel

||

Answers:

You can use a Boolean mask to figure out which columns you need.

First check if any of the values are 3 or 4, and then if not, check if any of the values are 2. Form the composite mask (variable m below) with an | (or) between those two conditions.

Finally you can NaN the False values, that way when you stack and groupby.agg(list) you’re left with just the column labels for the Trues.

cols = ['acne', 'wrinkles', 'darkspot']

m1 = df1[cols].isin([3, 4])
# If no `3` or `4` on the rows, check if there is a `2`
m2 = pd.DataFrame((~m1.any(1)).to_numpy()[:, None] &  df1[cols].eq(2).to_numpy(),
                   index=m1.index, columns=m1.columns)
m = (m1 | m2)
#    acne  wrinkles  darkspot
#0  False     False      True
#1   True      True     False
#2  False      True      True
#3  False      True      True

# Assignment aligns on original DataFrame index, i.e. `'level_0'`
df1['problem'] = m.where(m).stack().reset_index().groupby('level_0')['level_1'].agg(list)

print(df1)
     Name  acne  wrinkles  darkspot               problem
0     Tom     1         1         2            [darkspot]
1  Joseph     4         3         2      [acne, wrinkles]
2   Krish     1         4         3  [wrinkles, darkspot]
3    John     2         4         4  [wrinkles, darkspot]
Answered By: ALollz

You can use boolean mask:

problems = ['acne', 'wrinkles', 'darkspot']

m1 = df1[problems].isin([3, 4])  # main condition
m2 = df1[problems].eq(2)         # fallback condition
mask = m1 | (m1.loc[~m1.any(axis=1)] | m2)

df1['problem'] = mask.mul(problems).apply(lambda x: [i for i in x if i], axis=1)

Output:

>>> df1
     Name  acne  wrinkles  darkspot               problem
0     Tom     1         1         2            [darkspot]
1  Joseph     4         3         2      [acne, wrinkles]
2   Krish     1         4         3  [wrinkles, darkspot]
3    John     2         4         4  [wrinkles, darkspot]
Answered By: Corralien
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.