Błąd "can't set attribute" przy zmianie wartości DataFrame.style

0

Hej mam nastepujacy kod ktory koloruje maksymalne wartosci w danej kolumnie:

def highlight_max(x, color):
    return np.where(x == np.nanmax(x.to_numpy()), f"color: {color};", None)
df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])
df.style.apply(highlight_max, subset = ["A"], color='red', axis = 0)  
df.style.apply(highlight_max, subset = ["B"], color='blue', axis=0)  

Niestety zmiany sie nie zapisuja bo wartosci w kolumnie "A" nie sa pokolorowane tylko w kolumnie "B". Wiem ze moge stworzyc obiekt style = df.style i na nim pracowac ale chcialbym zeby mojego DataFrame'a zmienialo kilka funkcji wiec chce koniecznie zapisac zmiany w df.style. Ma ktos jakis pomysl?

0

Spróbuj tak:

def highlight_max(x, color):
    return np.where(x == np.nanmax(x.to_numpy()), f"color: {color};", None)

df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])
df.style.apply(highlight_max, subset = ["A"], color='red', axis = 0).apply(highlight_max, subset = ["B"], color='blue', axis=0)  
0
Robert Karpiński napisał(a):

Spróbuj tak:

def highlight_max(x, color):
    return np.where(x == np.nanmax(x.to_numpy()), f"color: {color};", None)

df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])
df.style.apply(highlight_max, subset = ["A"], color='red', axis = 0).apply(highlight_max, subset = ["B"], color='blue', axis=0)  

wtedy dziala ale ogolnie problem jest taki ze mam jedna funkcje ktora robi troche formatowania i ta funkcja zwraca df. Niestety tutaj nie mam pojecia jak zrobic zeby zwrocic df ze zmienionym stylem. Pozniej ten DataFrame chce uzyc w innej funkcji zeby zrobic jeszcze wiecej formatowania.

0

Styl jest "nakładany" na DataFrame zatem Twoja funkcja powinna zwracać Styler.
Ten styl można potem wykorzystać.

def highlight_max(x, color):
    return np.where(x == np.nanmax(x.to_numpy()), f"color: {color};", None)

df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])

styler = df.style.apply(highlight_max, subset = ["A"], color='red', axis = 0)\
.apply(highlight_max, subset = ["B"], color='blue', axis=0)  


# nowy DataFrame, ale korzystamy ze stylu poprzedniego
df2 = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])
styler2 = df2.style.use( styler.export())


styler2 

DataFrame ze stylu uzyskuje się:

styler2.data
0
Robert Karpiński napisał(a):

Styl jest "nakładany" na DataFrame zatem Twoja funkcja powinna zwracać Styler.
Ten styl można potem wykorzystać.

def highlight_max(x, color):
    return np.where(x == np.nanmax(x.to_numpy()), f"color: {color};", None)

df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])

styler = df.style.apply(highlight_max, subset = ["A"], color='red', axis = 0)\
.apply(highlight_max, subset = ["B"], color='blue', axis=0)  


# nowy DataFrame, ale korzystamy ze stylu poprzedniego
df2 = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])
styler2 = df2.style.use( styler.export())


styler2 

DataFrame ze stylu uzyskuje się:

styler2.data

Wciaz nie jest to na czym mi zalezy. Ponizej moj kod z komentarzami. Komenda use zawsze zwraca Styler zamiast df a ja chce df ale z nowym stylem tak zebym mogl go podawac jako argment do wielu funkcji i kazda z nich cos by w tym stylu zmieniala. W tym wypadku musialbym zamiast df jako argument podawac style a tego wolalbym nie robic

# Data
df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])

# Pierwsza funkcja ktora koloruje kolumne "A"
def f1(df):
    # Najprosciej by bylo gdyby dalo sie zrobic cos takiego ale nie sie da:
    # df.style = df.style.apply(highlight_max, subset = ["A"], color='red', axis = 0)
    
    # Wiec tworze styler
    styler = df.style
    styler = styler.apply(highlight_max, subset = ["A"], color='red', axis = 0)
    # Chce zeby funkcja zwrocila df ale ze zmienonym stylem. W tym momencie styler to odrebny obiekt. 
    # Niestety jak zwroce styler.data to dataframe nie ma tego stylu ktory wprowadzilem
    # return styler.data
    
    # Funkcje export i use nic mi nie daja bo wciaz jedyne co chce zwrocic to df ze zminionym stylem
    # To zwraca styl, a nie dataframe ze zmienionym stylem
    return df.style.use(styler.export())
    
    # Wydaje mi sie ze moj cel jest prosty i dziwi mnie ze jest to takie trudne 
    
# Druga funkcja ktora koloruje kolumne "B"
def f2(df):
    df.style.apply(highlight_max, subset = ["B"], color='blue', axis = 0)
    return df

res = f1(df)
type(res)
1

Bardzo upraszczając: df nie przechowuje stylu. df ma tylko interfejs do stylowania. To Styler przechowuje df i wie jak wyświetlić dane pokolorowane.
Bo df to tylko dane i nic więcej.

Twój kod można zmodyfikować do takiej postaci:

df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])

def f1(styler):
    return styler.apply(highlight_max, subset = ["A"], color='red', axis = 0)
    
def f2(styler):
    return styler.apply(highlight_max, subset = ["B"], color='blue', axis = 0)



style = df.style
f1(style)
f2(style)
style


# ale poniższe pokazuje dane już bez kolorowania bo został utworzony nowy "pusty" obiekt Styler, a nie ten teoretycznie "zapamiętany"
df.style

Najlepiej poczytać o tym tutaj: https://pandas.pydata.org/docs/reference/api/pandas.io.formats.style.Styler.html i https://mane-aajay.medium.com/style-your-pandas-dataframe-and-make-it-stunning-1e956d4a6672

1 użytkowników online, w tym zalogowanych: 0, gości: 1