Применить Pandas Styler к списку с несколькими индексами

#css #python-3.x #pandas #multi-index #pandas-styles

#css #python-3.x #pandas #многоиндексный #pandas-стили

Вопрос:

У меня есть сводная таблица с несколькими индексами, которую я создаю из фрейма данных, как показано ниже:

pivot_summary = pd.pivot_table(df, index=['Team', 'Associate Name'], columns=['Date'], values=['Call Transfers'], aggfunc=sum, fill_value=0, margins=True, margins_name='Grand Total')

Затем у меня есть функция под названием «Промежуточные итоги», которая вычисляет итоги для каждой команды и добавляет их на уровень 1 в мультииндекс.

 def SubTotals(df):
    '''
    The purpose of this function is to add sub-totals to each individual team 
    '''
    #create a dataframe with the totals of each team
    sub_totals = df.sum(level=0)
    #create variable that stores string of team #   'Total'
    sub_totals_str = sub_totals.index.str[:]   ' Total'
    
    #add the subtotals for each team to the main dataframe
    final_df = df.append(sub_totals.assign(Team=sub_totals_str).set_index('Team', append=True)).sort_index()
    
    #drop the duplicate "Grand-Total" row at the bottom
    final_df.drop(final_df.tail(1).index, inplace=True)
    
    #create string variables of both index(Multi-level index) to zip into tuples
    index_level_0 = sub_totals.index.tolist()
    index_level_1 = sub_totals_str.tolist()
    
    #remove duplicate "Grand Total" from second index
    index_level_1[-1] = ''     
    
    #zip index to pass through formatting function
    team_totals_index = list(zip(index_level_0,index_level_1))
    
    return final_df, team_totals_index
  

Пока все хорошо, я получаю промежуточные итоги для каждой команды именно так, как я хочу. Я застрял в том, что хочу выделить все строки, которые находятся в переменной «teams_totals_index». Я пытаюсь создать функцию «highlight_rows», которая принимает список мультииндексов в качестве параметра и выделяет, если есть совпадение. Я видел много примеров, где вместо этого используется лямбда, и это послужило мне грубой проверкой, например:
final_df.style.apply(lambda x: ['background: lightgreen' if x.name==('Grand Total','') else '' for i in x], axis=1)

выдает результат, который я ищу, он полностью выделяет строку с несколькими индексами («Общий итог», «), однако я также хочу выделить все остальные команды. Я не хочу жестко кодировать каждый индекс в лямбда-функцию, поскольку количество команд является динамическим.

Я пытался использовать лямбда-функцию в качестве примера для создания своего собственного, но у меня ничего не получается. ниже то, что я попытался.

 def highlight_rows(x, index_list):
    '''
    highlight the maximum Totals of each team and Grand Total.
    '''
    df_styler = x.copy()
    
    b_color = 'background: lightgreen'
    
    for index_0, index_1 in index_list:
        df_styler.loc[index_0, index_1] = b_color
        
    return df_styler

  

Затем я пытаюсь применить final_df.style.apply(highlight_rows, index_list=team_totals_index, axis=1)

но я получаю ошибку ValueError: несоответствие длины, ожидаемое значение 32, новая ось содержит 22 элемента

Я также пытался:

 def highlight_rows(x, index_list):
    '''
    highlight the maximum Totals of each team and Grand Total.
    '''
    
      
    for index_0, index_1 in index_list:
        if x.name== (index_0, index_1):
            return ['background: lightgreen']
        else:
            return ''

  

Но я также получаю ту же ошибку ValueError: несоответствие длины, ожидаемое значение 1 элемент, новое значение имеет 22 элемента.

По сути, я хочу выполнить итерацию по моему списку «total_teams_index», который содержит многоуровневый индекс, который я хочу выделить. Буду признателен за любую информацию о том, как я мог бы это сделать.

Ответ №1:

Хотя я не могу структурировать это как функцию и заставить ее работать, я нашел работоспособное решение без необходимости жестко кодировать каждую пару индексов. Я использую цепочки методов для создания стиля по частям, как это предлагается в документации.

 final_df.style.
    apply(lambda x: ['background-color: #FF9900'
                     if x.name in team_totals_index else 'background-color:#FFCC99' for i in x], axis=1).
    apply(lambda x: ['font-weight: bold' if x.name in team_totals_index else '' for i in x], axis=1).
    apply(lambda x: ['border-top-style:solid' if x.name in team_totals_index else '' for i in x], axis=1).
    apply(lambda x: ['border-bottom-style:solid' if x.name in team_totals_index else '' for i in x], axis=1)
  

Лично мне не нравится повторяющееся использование одного и того же цикла, но он служит моей цели и стилизует все именно так, как нужно. Ключ к тому, чтобы заставить все это работать, заключался if x.name in [list containing index pairs] в том, чтобы не пытаться перебирать каждую пару индексов, как показано в моих примерах выше. Я надеюсь, что это может помочь любому, кто сталкивается и все еще может потенциально использовать менее подробный / функциональный способ структурирования этого.