对 HTML 导出的 DataFrame 应用了多个格式规则?
Multiple formatting rules applied on HTML-exported DataFrame?
我需要通过函数将 pandas
DataFrame
粘贴到新生成的电子邮件草稿的 HTML 正文中。问题是,在电子邮件草稿结束之前,我需要对其应用多个条件样式。
import datetime as dt
import pandas as pd
import win32com.client as win32
def create_mail(text, subject, recipient):
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = recipient
mail.Subject = subject
mail.HtmlBody = text
mail.save()
df = pd.DataFrame(data = {'Number': [100.00, -100.00], 'Date': [dt.date(2020, 1, 1), dt.date(2022, 1, 1)]})
df['Date'] = pd.to_datetime(df['Date']).dt.date
样式定义如下:
def style_negative(v, props = 'color:red;'):
return props if v < 0 else None
def style_red_date(v, props = 'color:red;'):
return props if v < dt.datetime.now().date() else None
Number
列中的负数必须为红色。
Date
列中今天之前的日期也必须标为红色。
如果我通过 df.style.applymap()
只对 DataFrame
对象应用一种(任一)样式,它工作得很好。但是,当我想通过 .apply()
将另一种样式应用于(现在)Styler
对象时
df = df.style.applymap(style_negative, subset = ['Number'])
df = df.apply(style_red_date, subset = ['Date'])
我收到以下错误:
ValueError: The truth value of a Series is ambiguous. Use a.empty,
a.bool(), a.item(), a.any() or a.all().
我的其余代码:
html = (
df
.format({'Number':"{:.2f}"})
.set_properties(**{'font-size':'11pt'})
.hide_index()
.render()
)
mail_content = """
<html><head></head><body>
{0}
</body></html>
""".format(html)
create_mail(mail_content, "Subject", "")
我已经通读了 this 关于从 Styler
对象导出样式的问题,但它似乎只能用于 DataFrame
对象。有没有办法将多个格式规则应用于 DataFrame
/ Styler
对象并将其导出为 HTML?我是不是遗漏了一些微不足道的东西?
我们在这里只需要 np.where
而不是内联 if else
。
一些其他注意事项:
- 创建 Styler object from
DataFrame.style
we should save that as a variable called styler
or something similar instead of df
to denote the type of object has changed from DataFrame to Styler
- 在向已声明的 Styler 对象添加额外样式时,我们不需要重新分配
- 我们需要
np.where
创建一个 ndarray 样式,基于对 Series[= 进行布尔运算所产生的所有布尔值64=]
- 我们应该更喜欢空字符串 (
''
) 来表示无样式,而不是 None
(为了一致的 dtype 处理)
- 与其绑定使用 python 字符串格式,我们应该使用(从 pandas 1.3.0 开始)
Styler.to_html
with doctype_html=True
instead of Styler.render
# import numpy as np
def style_negative(v, props='color:red;'):
return np.where(v < 0, props, '')
def style_red_date(v, props='color:red;'):
return np.where(v < dt.datetime.now().date(), props, '')
styler = df.style.applymap(style_negative, subset=['Number'])
styler.apply(style_red_date, subset=['Date'])
mail_content = (
styler
.format({'Number': "{:.2f}"})
.set_properties(**{'font-size': '11pt'})
.hide_index()
.to_html(doctype_html=True)
)
我们也可以做一个样式链,例如:
# Using same modified functions as above but as a function chain:
mail_content = df.style.applymap(
style_negative, subset=['Number']
).apply(
style_red_date, subset=['Date']
).format(
{'Number': "{:.2f}"}
).set_properties(
**{'font-size': '11pt'}
).hide_index().to_html(doctype_html=True)
这些选项在 mail_content
中产生以下 HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="">
<style type="text/css">
#T_0f6b6_row0_col0,
#T_0f6b6_row1_col1 {
font-size: 11pt;
}
#T_0f6b6_row0_col1,
#T_0f6b6_row1_col0 {
color: red;
font-size: 11pt;
}
</style>
</head>
<body>
<table id="T_0f6b6_">
<thead>
<tr>
<th class="col_heading level0 col0">Number</th>
<th class="col_heading level0 col1">Date</th>
</tr>
</thead>
<tbody>
<tr>
<td id="T_0f6b6_row0_col0" class="data row0 col0">100.00</td>
<td id="T_0f6b6_row0_col1" class="data row0 col1">2020-01-01</td>
</tr>
<tr>
<td id="T_0f6b6_row1_col0" class="data row1 col0">-100.00</td>
<td id="T_0f6b6_row1_col1" class="data row1 col1">2022-01-01</td>
</tr>
</tbody>
</table>
</body>
</html>
设置:
import datetime as dt
import numpy as np
import pandas as pd
# Reproducible example with year offset so styles will always
# be reproducible same even in future years
# (even though date values will change)
df = pd.DataFrame({
'Number': [100.00, -100.00],
'Date': pd.to_datetime(
[dt.date(dt.datetime.now().year - 1, 1, 1),
dt.date(dt.datetime.now().year + 1, 1, 1)]
).date
})
我需要通过函数将 pandas
DataFrame
粘贴到新生成的电子邮件草稿的 HTML 正文中。问题是,在电子邮件草稿结束之前,我需要对其应用多个条件样式。
import datetime as dt
import pandas as pd
import win32com.client as win32
def create_mail(text, subject, recipient):
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = recipient
mail.Subject = subject
mail.HtmlBody = text
mail.save()
df = pd.DataFrame(data = {'Number': [100.00, -100.00], 'Date': [dt.date(2020, 1, 1), dt.date(2022, 1, 1)]})
df['Date'] = pd.to_datetime(df['Date']).dt.date
样式定义如下:
def style_negative(v, props = 'color:red;'):
return props if v < 0 else None
def style_red_date(v, props = 'color:red;'):
return props if v < dt.datetime.now().date() else None
Number
列中的负数必须为红色。
Date
列中今天之前的日期也必须标为红色。
如果我通过 df.style.applymap()
只对 DataFrame
对象应用一种(任一)样式,它工作得很好。但是,当我想通过 .apply()
Styler
对象时
df = df.style.applymap(style_negative, subset = ['Number'])
df = df.apply(style_red_date, subset = ['Date'])
我收到以下错误:
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
我的其余代码:
html = (
df
.format({'Number':"{:.2f}"})
.set_properties(**{'font-size':'11pt'})
.hide_index()
.render()
)
mail_content = """
<html><head></head><body>
{0}
</body></html>
""".format(html)
create_mail(mail_content, "Subject", "")
我已经通读了 this 关于从 Styler
对象导出样式的问题,但它似乎只能用于 DataFrame
对象。有没有办法将多个格式规则应用于 DataFrame
/ Styler
对象并将其导出为 HTML?我是不是遗漏了一些微不足道的东西?
我们在这里只需要 np.where
而不是内联 if else
。
一些其他注意事项:
- 创建 Styler object from
DataFrame.style
we should save that as a variable calledstyler
or something similar instead ofdf
to denote the type of object has changed from DataFrame to Styler - 在向已声明的 Styler 对象添加额外样式时,我们不需要重新分配
- 我们需要
np.where
创建一个 ndarray 样式,基于对 Series[= 进行布尔运算所产生的所有布尔值64=] - 我们应该更喜欢空字符串 (
''
) 来表示无样式,而不是None
(为了一致的 dtype 处理) - 与其绑定使用 python 字符串格式,我们应该使用(从 pandas 1.3.0 开始)
Styler.to_html
withdoctype_html=True
instead ofStyler.render
# import numpy as np
def style_negative(v, props='color:red;'):
return np.where(v < 0, props, '')
def style_red_date(v, props='color:red;'):
return np.where(v < dt.datetime.now().date(), props, '')
styler = df.style.applymap(style_negative, subset=['Number'])
styler.apply(style_red_date, subset=['Date'])
mail_content = (
styler
.format({'Number': "{:.2f}"})
.set_properties(**{'font-size': '11pt'})
.hide_index()
.to_html(doctype_html=True)
)
我们也可以做一个样式链,例如:
# Using same modified functions as above but as a function chain:
mail_content = df.style.applymap(
style_negative, subset=['Number']
).apply(
style_red_date, subset=['Date']
).format(
{'Number': "{:.2f}"}
).set_properties(
**{'font-size': '11pt'}
).hide_index().to_html(doctype_html=True)
这些选项在 mail_content
中产生以下 HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="">
<style type="text/css">
#T_0f6b6_row0_col0,
#T_0f6b6_row1_col1 {
font-size: 11pt;
}
#T_0f6b6_row0_col1,
#T_0f6b6_row1_col0 {
color: red;
font-size: 11pt;
}
</style>
</head>
<body>
<table id="T_0f6b6_">
<thead>
<tr>
<th class="col_heading level0 col0">Number</th>
<th class="col_heading level0 col1">Date</th>
</tr>
</thead>
<tbody>
<tr>
<td id="T_0f6b6_row0_col0" class="data row0 col0">100.00</td>
<td id="T_0f6b6_row0_col1" class="data row0 col1">2020-01-01</td>
</tr>
<tr>
<td id="T_0f6b6_row1_col0" class="data row1 col0">-100.00</td>
<td id="T_0f6b6_row1_col1" class="data row1 col1">2022-01-01</td>
</tr>
</tbody>
</table>
</body>
</html>
设置:
import datetime as dt
import numpy as np
import pandas as pd
# Reproducible example with year offset so styles will always
# be reproducible same even in future years
# (even though date values will change)
df = pd.DataFrame({
'Number': [100.00, -100.00],
'Date': pd.to_datetime(
[dt.date(dt.datetime.now().year - 1, 1, 1),
dt.date(dt.datetime.now().year + 1, 1, 1)]
).date
})