Pandas 和 XlsxWriter:如何更改格式并从数据框中的项目添加注释

Pandas and XlsxWriter: how to alter formatting and add comments from items in dataframe

我有一组数据框,经过大量修改后,它们对数据是否良好进行了评论和标记:

df = pd.DataFrame({'Name': ['a','a','b','b'],
                   'Measurements': ['temp','pressure','temp','pressure'],
                   'Values': [1, 2, -1, np.nan],
                   'Comment': ['','','Is negative', 'Is NaN'],
                   'IsBad':[False, False, True, True] })
measurements = df.reset_index().pivot(index='Name',columns='Measurements',values='Values')
comments = df.reset_index().pivot(index='Name',columns='Measurements',values='Comment')
bad_cells = df.reset_index().pivot(index='Name',columns='Measurements',values='IsBad')

我想做的是将其导出到一个 excel 文件,该文件的单元格格式为“IsBad”,并插入一条注释以解释单元格为何错误(忽略此示例开始在 A2...):

我一直在寻找几种不同的方法来做到这一点,但我被卡住了,我可以为每个旋转数据框创建一个带有新 sheet 的 excel 文件,但我不能'似乎无法使用其他 sheet 作为输入来创建评论。 XlsxWriter 似乎也不太擅长在循环中创建 excel 文件,在循环中我可以根据“df”对象中的行定义每个单元格。

有什么想法吗?

我最终得到了这个:

import pandas as pd
import xlsxwriter

df = pd.DataFrame({'Name': ['a','a','b','b'],
                   'Measurements': ['temp','pressure','temp','pressure'],
                   'Values': [1, 2, -1, -999],
                   'Comment': ['','','Is negative', 'Is NaN'],
                   'IsBad':[False, False, True, True] })
                   
measurements = df.reset_index().pivot(index='Name',columns='Measurements',values='Values')
comments = df.reset_index().pivot(index='Name',columns='Measurements',values='Comment')
bad_cells = df.reset_index().pivot(index='Name',columns='Measurements',values='IsBad')

wb = xlsxwriter.Workbook('measurements.xlsx')
ws = wb.add_worksheet()
bg = wb.add_format({'bg_color': 'yellow'})

ws.write('A1', 'Name')
ws.write('B1', 'pressure')
ws.write('C1', 'temp')

i = 2
for key, row in measurements.iterrows():
    ws.write('A' + str(i), key)
    ws.write('B' + str(i), row[0])
    ws.write('C' + str(i), row[1])

    if bad_cells['pressure'].index.get_loc(key):
        ws.write('B' + str(i), row[0], bg)
        ws.write_comment('B' + str(i), comments['pressure'][i - 2]) 
    
    if bad_cells['temp'].index.get_loc(key):
        ws.write('C' + str(i), row[1], bg)
        ws.write_comment('C' + str(i), comments['temp'][i - 2]) 
    i += 1

wb.close()

我不得不将 np.nan 更改为 -999,因为无法在 excel 中写入 NaN 值。 无论如何,您可以尝试其他替换: xlsxwriter docs

Alex Montano 的回答很好,但由于在列 names/indexes 上进行了一些硬编码,因此在我的真实数据上效果不佳。我在这里发布了更通用的解决方案(基于他的工作)。我还从另一个 Whosebug 问题中引入了一个函数,它允许超过 26 列的表具有 AA、AB、AC...列:

import pandas as pd
import xlsxwriter

df = pd.DataFrame({'Name': ['a','a','b','b'],
                   'Measurements': ['temp','pressure','temp','pressure'],
                   'Values': [1, 2, -1, -999],
                   'Comment': ['','','Is negative', 'Is NaN'],
                   'IsBad':[False, False, True, True] })
                   
measurements = df.reset_index().pivot(index='Name',columns='Measurements',values='Values')
comments = df.reset_index().pivot(index='Name',columns='Measurements',values='Comment')
bad_cells = df.reset_index().pivot(index='Name',columns='Measurements',values='IsBad')

# 
def excel_cols():
    n = 1
    while True:
        yield from (''.join(group) for group in itertools.product(string.ascii_uppercase, repeat=n))
        n += 1

wb = xlsxwriter.Workbook('my_file.xlsx',{'strings_to_numbers': True})

ws = wb.add_worksheet('QC')
bad_format = wb.add_format({'bg_color': 'yellow'})

# Write column names, reset index so that they're just another column
xl_df  = measurements.reset_index().astype(object).replace(np.nan, '')
column_index = list(itertools.islice(excel_cols(), len(xl_df.columns)))

# Create cell index values in Excel format ...X,Y,Z,AA,AB... etc
for column_ix, column in zip(column_index, xl_df.columns):
    ws.write(column_ix + '1', column)

i = 2
for key, row in xl_df.iterrows():
    for column_ix, column in zip(column_index, row.index):
        this_index = column_ix + str(i)
        cell_value = row[column]
        this_cell_is_bad = bad_cells.reset_index()[column].iloc[key]
        this_comment = comments.reset_index()[column].iloc[key]
        
        if this_cell_is_bad == True:
            ws.write(this_index, cell_value, bad_format)
            ws.write_comment(this_index, this_comment)
        else:
            ws.write(this_index, cell_value)  
    i += 1

wb.close()