applying a method to a few selected columns in a pandas dataframe

Question:

I would like to apply a little method to several columns in a Dataframe. The method color_negative can’t be applied to columns with strings, therefore I need to skip these columns somehow. I could think of two approaches to solve the problem, though sadly none is working.

  • In approach 1:

    I try to apply the method to every column one by one skipping the first one by using the index of the Dataframe and by setting the incrementing counter of the while loop to 1. When executing this approach I get the error, that the ‘Series’ object has no attribute ‘style’, so apparently, I can’t apply a method to a single column.

  • In approach 2:

    I try to use the subset to apply the method only to those columns with numeric values, though I’m not sure if I use subset correctly. When executing this approach I get the error that the object of type ‘Styler’ has no len().

Here a simplified example:

import pandas as pd

d = {'col1': ['a', 'b'], 'col2': [21, 22], 'col3': [3, 51]}
df = pd.DataFrame(data=d)

def color_negative_red(val):
    color = 'black'
    if val < -1 : color = 'red'
    if val > 1 :  color = 'green'
    return 'color: %s' % color    
    
i=1
while i <= len(df):
    #Approach 1
    df.iloc[:, i] = df.iloc[:, i].style.applymap(color_negative_red)
    #Approach 2
    df = df.style.applymap(color_negative_red, subset = df.iloc[:, i])
    i+=1    

df

Has anyone a suggestion on how to solve this problem?

Asked By: BlueBerry

||

Answers:

you can select the wanted columns and then applymap on them, as such:

column_names = ['name_a','name_b']
df[column_names] = df[column_names].applymap(my_func)

if you want you can filter out columns that are string

from numpy.core.multiarray import dtype

column_names = [name for name,col_type in df.dtypes.items() if col_type!=dtype('O')]
Answered By: moshevi

You can use style.Styler.apply with DataFrame of styles with numpy.select for filling:

d = {'col1': ['a', 'b'], 'col2': [21, 22], 'col3': [0, -51]}
df = pd.DataFrame(data=d)

def color_negative_red(x):
    #select only numeric columns
    x1 = x.select_dtypes(np.number)
    c1 = 'color: red'
    c2 = 'color: green'
    c3 = '' 
    #boolean masks
    m1 = x1 < -1
    m2 = x1 > 1
    #numpy array by conditions
    arr = np.select([m1, m2], [c1, c2], default=c3)
    df1 =  pd.DataFrame(arr, index=df.index, columns=x1.columns)
    #added strings columns filled by c3 string 
    df1 = df1.reindex(columns=x.columns, fill_value=c3)
    return df1

df.style.apply(color_negative_red, axis=None)

pic

Answered By: jezrael

Vectorize your function

import numpy as np

f = np.vectorize(color_negative_red)

Then you can use simple apply, while filtering by the column name as desired:

df.apply(lambda x: f(x) if x.name not in ['col1'] else x)
#   col1          col2          col3
# 0    a  color: green  color: green
# 1    b  color: green  color: green
Answered By: Artem Sokolov
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.