使用to_html将CSS类应用于Pandas DataFrame

azpvetkf  于 2023-05-05  发布在  其他
关注(0)|答案(7)|浏览(194)

我在使用“classes”参数和Pandas的“to_html”方法对DataFrame进行样式化时遇到了问题。
“classes:str or list or tuple,default None CSS class(es)to apply to the result html table”from:https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.to_html.html
我可以像这样渲染一个样式化的DataFrame(例如):

df = pd.DataFrame([[1, 2], [1, 3], [4, 6]], columns=['A', 'B'])

myhtml = df.style.set_properties(**{'font-size': '11pt', 'font-family': 'Calibri','border-collapse': 'collapse','border': '1px solid black'}).render()

with open('myhtml.html','w') as f:
    f.write(myhtml)

我如何使用“classes”和“to_html”来样式化DataFrame的html输出,如下所示:

df.to_html('myhtml.html',classes=<something here>)
sy5wg1nm

sy5wg1nm1#

Pandas的to_html只是输出一个包含HTML表格标记的大字符串。classes参数是一个方便的处理程序,用于为<table>给予一个 class 属性,该属性将在以前创建的样式化它的CSS文档中引用。因此,将to_html合并到引用外部CSS的更广泛的HTML文档构建中。
有趣的是,to_html增加了双重类<table class="dataframe mystyle">,可以在CSS中单独引用,.dataframe {...} .mystyle{...},或者一起引用.dataframe.mystyle {...}。下面用随机数据进行说明。

数据

import pandas as pd
import numpy as np

pd.set_option('display.width', 1000)
pd.set_option('colheader_justify', 'center')

np.random.seed(6182018)
demo_df = pd.DataFrame({'date': np.random.choice(pd.date_range('2018-01-01', '2018-06-18', freq='D'), 50),
                        'analysis_tool': np.random.choice(['pandas', 'r', 'julia', 'sas', 'stata', 'spss'],50),              
                        'database': np.random.choice(['postgres', 'mysql', 'sqlite', 'oracle', 'sql server', 'db2'],50), 
                        'os': np.random.choice(['windows 10', 'ubuntu', 'mac os', 'android', 'ios', 'windows 7', 'debian'],50), 
                        'num1': np.random.randn(50)*100,
                        'num2': np.random.uniform(0,1,50),                   
                        'num3': np.random.randint(100, size=50),
                        'bool': np.random.choice([True, False], 50)
                       },
                        columns=['date', 'analysis_tool', 'num1', 'database', 'num2', 'os', 'num3', 'bool']
          )

print(demo_df.head(10))
#      date    analysis_tool     num1      database     num2        os      num3  bool 
# 0 2018-04-21     pandas     153.474246       mysql  0.658533         ios   74    True
# 1 2018-04-13        sas     199.461669      sqlite  0.656985   windows 7   11   False
# 2 2018-06-09      stata      12.918608      oracle  0.495707     android   25   False
# 3 2018-04-24       spss      88.562111  sql server  0.113580   windows 7   42   False
# 4 2018-05-05       spss     110.231277      oracle  0.660977  windows 10   76    True
# 5 2018-04-05        sas     -68.140295  sql server  0.346894  windows 10    0    True
# 6 2018-05-07      julia      12.874660    postgres  0.195217         ios   79    True
# 7 2018-01-22          r     189.410928       mysql  0.234815  windows 10   56   False
# 8 2018-01-12     pandas    -111.412564  sql server  0.580253      debian   30   False
# 9 2018-04-12          r      38.963967    postgres  0.266604   windows 7   46   False

CSS*(保存为df_style.css)*

/* includes alternating gray and white with on-hover color */

.mystyle {
    font-size: 11pt; 
    font-family: Arial;
    border-collapse: collapse; 
    border: 1px solid silver;

}

.mystyle td, th {
    padding: 5px;
}

.mystyle tr:nth-child(even) {
    background: #E0E0E0;
}

.mystyle tr:hover {
    background: silver;
    cursor: pointer;
}

Pandas

pd.set_option('colheader_justify', 'center')   # FOR TABLE <th>

html_string = '''
<html>
  <head><title>HTML Pandas Dataframe with CSS</title></head>
  <link rel="stylesheet" type="text/css" href="df_style.css"/>
  <body>
    {table}
  </body>
</html>.
'''

# OUTPUT AN HTML FILE
with open('myhtml.html', 'w') as f:
    f.write(html_string.format(table=demo_df.to_html(classes='mystyle')))

输出
HTML*(引用df_style.css,假设在同一目录下;见表中的类参数)*

<html>
  <head><title>HTML Pandas Dataframe with CSS</title></head>
  <link rel="stylesheet" type="text/css" href="df_style.css"/>
  <body>
    <table border="1" class="dataframe mystyle">
  <thead>
    <tr style="text-align: center;">
      <th></th>
      <th>date</th>
      <th>analysis_tool</th>
      <th>num1</th>
      <th>database</th>
      <th>num2</th>
      <th>os</th>
      <th>num3</th>
      <th>bool</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2018-04-21</td>
      <td>pandas</td>
      <td>153.474246</td>
      <td>mysql</td>
      <td>0.658533</td>
      <td>ios</td>
      <td>74</td>
      <td>True</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2018-04-13</td>
      <td>sas</td>
      <td>199.461669</td>
      <td>sqlite</td>
      <td>0.656985</td>
      <td>windows 7</td>
      <td>11</td>
      <td>False</td>
    </tr>
    <tr>
      <th>2</th>
      <td>2018-06-09</td>
      <td>stata</td>
      <td>12.918608</td>
      <td>oracle</td>
      <td>0.495707</td>
      <td>android</td>
      <td>25</td>
      <td>False</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2018-04-24</td>
      <td>spss</td>
      <td>88.562111</td>
      <td>sql server</td>
      <td>0.113580</td>
      <td>windows 7</td>
      <td>42</td>
      <td>False</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2018-05-05</td>
      <td>spss</td>
      <td>110.231277</td>
      <td>oracle</td>
      <td>0.660977</td>
      <td>windows 10</td>
      <td>76</td>
      <td>True</td>
    </tr>
    ...
  </tbody>
</table>
  </body>
</html>

zzwlnbp8

zzwlnbp82#

本质上,pandas.to_html()只是导出一个普通的HTML表。您可以将表格插入正文中的任何位置,并通过样式部分中的CSS控制样式。

<html>
<head>
<style> 
  table, th, td {{font-size:10pt; border:1px solid black; border-collapse:collapse; text-align:left;}}
  th, td {{padding: 5px;}}
</style>
</head>
<body>
{
  pandas.to_html()
}
</body>
</html>
eanckbw9

eanckbw93#

我发现最精确的,坦率地说最简单的方法是跳过样式,to_html()等。以及使用df.to_dict()方法将DF转换为字典。
具体来说,给我带来麻烦的是在outlook电子邮件中显示样式化的pandas html,因为pandas产生的css混乱无法正确呈现。
迭代dict并在那里生成html,只需将键/值 Package 在您需要的标签中,添加类等。并将其连接成一个字符串。然后粘贴这个字符串到一个准备好的模板与预定义的css。
为了方便起见,我发现导出相同的df两次是很有用的,使用.to_dict()和to_dict('index')首先填充列,然后逐行向下工作。或者,只需要一个相关列名的列表。

dict_data = [df.to_dict(), df.to_dict('index')]

return_str = '<table><tr>'

for key in dict_data[0].keys():
    return_str = return_str + '<th class="header">' + key + '</th>'

return_str = return_str + '</tr>'

for key in dict_data[1].keys():
    return_str = return_str + '<tr><th class="index">' + key + '</th>'
    for subkey in dict_data[1][key]:
        return_str = return_str + '<td>' + dict_data[1][key][subkey] + '</td>'

return_str = return_str + '</tr></table>'

然后return_str进入模板。

yrefmtwq

yrefmtwq4#

这要归功于X1 E0 F1 X的回答--我能够将他们的解决方案定制成更精确的东西。我个人喜欢根据某些值有条件地格式化我的表格。
我发现生成你自己的HTML是最精确的方法,给你完全的控制。

##note how any row that has the drop alert flag set to "Y" will be formatted yellow:

dict_data = [df.to_dict(), df.to_dict('index')]

htmldf = '<table><tr>'

for key in dict_data[0].keys():
    htmldf = htmldf + '<th class="header">' + key + '</th>'

htmldf = htmldf + '</tr>'

for key in dict_data[1].keys():
    htmldf = htmldf + '<tr '
    htmldf = htmldf + 'style="font-weight: bold; background-color: yellow">' if dict_data[1][key]['drop_alert'] == 'Y' else htmldf + '>'
    for subkey in dict_data[1][key]:
        htmldf = htmldf + '<td>' + str(dict_data[1][key][subkey]) + '</td>'
    htmldf = htmldf + '</tr>'

htmldf = htmldf + '</tr></table>'

# Write html object to a file (adjust file path; Windows path is used here)
with open('C:\\Users\\Documents\\test.html','wb') as f:
    f.write(htmldf.encode("UTF-8"))

**结果:**整齐的条件格式表

a9wyjsp7

a9wyjsp75#

我是这么做的
为css代码创建一个文本文件,并在这里写下你的css代码,比如css_style. txt现在将这个txt文件作为一个字符串在你的python文件中读取
with open('css_style.txt', 'r') as myfile: style = myfile.read()
现在在HTML代码中

"""<html><head>Something Something</head>{1}<div>{0}</div></html>""".format(some_panda_dataframe.to_html,style)

在我的例子中,css_style.txt文件是

<style>
table {
  border-collapse: collapse;
  width: 100%;
}

th {
  text-align: center;
  padding: 8px;
}

td {
  text-align: left;
  padding: 8px;
}

tr:nth-child(even){background-color: #FFD5D5}

th {
  background-color: #0000FF;
  color: white;
}
</style>
cs7cruho

cs7cruho6#

为了添加到我早期的to_htmlanswer中,新的Pandas 1.3.0+ to_xml可以仅使用样式表(即CSS和XSLT)呈现HTML文档,而无需任何字符串格式。
虽然XSLT在复制相同的HTML表设计时会有点复杂,但它对于用户定义的更改是开放的。

数据

import pandas as pd
import numpy as np

np.random.seed(1032022)
demo_df = pd.DataFrame({
    'date': np.random.choice(pd.date_range('2021-01-01', '2021-12-31', freq='D'), 50),
    'analysis_tool': np.random.choice(['pandas', 'r', 'julia', 'sas', 'stata', 'spss'],50),
    'num1': np.random.randn(50)*100,
    'database': np.random.choice(['postgres', 'mysql', 'sqlite', 'oracle', 'sql server', 'db2'],50),
    'num2': np.random.uniform(0,1,50),
    'os': np.random.choice(['windows 10', 'ubuntu', 'mac os', 'android', 'ios', 'windows 7', 'debian'],50),                    
    'num3': np.random.randint(100, size=50),
    'bool': np.random.choice([True, False], 50)
})

print(demo_df.head(10))
#         date analysis_tool        num1  ...          os  num3   bool
# 0 2021-05-02         stata   52.370960  ...  windows 10    36  False
# 1 2021-03-16        pandas -135.411727  ...     android    74  False
# 2 2021-12-17           sas  -56.823191  ...      debian    75  False
# 3 2021-11-11        pandas  -32.575253  ...      debian    33  False
# 4 2021-11-19         julia  176.464891  ...      mac os    63   True
# 5 2021-12-30             r  -82.874595  ...      ubuntu    52   True
# 6 2021-03-27             r   63.897578  ...     android    56  False
# 7 2021-03-14         julia  -75.117220  ...      mac os     6  False
# 8 2021-04-09          spss -302.664890  ...         ios    97   True
# 9 2021-03-15          spss  -12.014122  ...         ios    27   True

CSS*(保存为DataFrameStyle.css)*

/* includes alternating gray and white with on-hover color */

.mystyle {
    font-size: 11pt; 
    font-family: Arial;
    border-collapse: collapse; 
    border: 1px solid silver;

}

.mystyle td, th {
    padding: 5px;
}

.mystyle tr:nth-child(even) {
    background: #E0E0E0;
}

.mystyle tr:hover {
    background: silver;
    cursor: pointer;
}

XSLT*(保存为DataFrameStyle.xsl;references .css)*

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" omit-xml-declaration="no" indent="yes"/>

    <xsl:template match="/data">
      <html>
        <head><title>HTML Pandas Dataframe with CSS</title></head>
        <link rel="stylesheet" type="text/css" href="DataFrameStyle.css"/>
        <body>
          <table border="1" class="dataframe mystyle">
            <thead>
                <tr style="text-align: center;">
                    <xsl:apply-templates select="row[1]/*" mode="headers"/>
                </tr>
            </thead>
            <tbody>
                <xsl:apply-templates select="row"/>
            </tbody>
          </table>
        </body>
      </html>
    </xsl:template>

    <xsl:template match="row[1]/*" mode="headers">
        <th><xsl:value-of select="local-name()"/></th>
    </xsl:template>

    <xsl:template match="row">
        <tr><xsl:apply-templates select="*"/></tr>
    </xsl:template>
    
    <xsl:template match="row/*">
       <td><xsl:value-of select="."/></td>
    </xsl:template>

</xsl:stylesheet>

Pandas

demo_df.to_xml(
    "/path/to/Output.html",
    stylesheet = "DataFrameStyle.xsl"
)

输出

t30tvxxf

t30tvxxf7#

由于pandas to_html缺少功能

使用下面的代码,你可以重复列作为<tr>属性,这对于静止,写事件等是必不可少的。

参数

  • row_attrs(列表,可选):要作为属性写入行<tr>元素的列列表。默认为无。
  • row_cols(列表,可选):要作为子元素写入<td>元素的row元素中的列的列表。默认为所有列。
import xml.etree.ElementTree as etree

def dataframe_to_html(df, row_attrs=[], row_cols=None):
    """
    Converts dataframe to an html <table> as an ElementTree class.  
        * df (pandas.DataFrame): table
        * row_attrs (list, optional): List of columns to write as attributes in <tr> row element. Defaults to [] none.
        * row_cols (list, optional): List of columns to write as children in row <td> element. Defaults to all columns.               
    - returns: ElementTree class containing an html <table>      
    Note: generate a string with `etree.tostring(dataframe_to_html(...), encoding='unicode', method='xml')`
    """
    if not row_cols: # default to use all columns as sub-elements of row
        row_cols = df.columns.to_list()   
    table = df.astype(str) # turns everything on str
    table_dict = table.to_dict('split')
    col2index = { v:i for i, v in enumerate(table_dict['columns']) }    
    def add_rows(root, table_dict, row_attrs_, row_cols_, tag_row='tr', tag_col='td'):            
        for row in table_dict:
            # row attrs names and values in lower-case (key:value)
            row_attrs = { key.lower(): row[col2index[key]].lower() for key in row_attrs_ } 
            erow = etree.SubElement(root, tag_row, attrib=row_attrs) 
            for col in row_cols_:
                ecol = etree.SubElement(erow, tag_col)
                ecol.text = str(row[col2index[col]])
    etable = etree.Element('table')
    thead = etree.SubElement(etable, 'thead') 
    add_rows(thead, [table_dict['columns']], [], row_cols, 'tr', 'th')
    tbody = etree.SubElement(etable, 'tbody')     
    add_rows(tbody, table_dict['data'], row_attrs, row_cols)
    return etable

用法

...
# manipulate your dataframe and create `row_attrs` and `row_cols`
html_table = dataframe_to_html(table, row_attrs, row_cols)
# then convert your etree to string to use on flask template for example
html_table = etree.tostring(html_table, encoding='unicode', method='xml')
render_template('index.html', pandas_table=html_table...) # your template variables

注意:<tr>行属性名为小写。

进一步建议:表上的其他自定义仍然可以使用etree包中的ElementTree完成。

相关问题