#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]
в том, чтобы не пытаться перебирать каждую пару индексов, как показано в моих примерах выше. Я надеюсь, что это может помочь любому, кто сталкивается и все еще может потенциально использовать менее подробный / функциональный способ структурирования этого.