没有和有样式的电子邮件数据框
Email dataframes without and with Styling
我正在 运行 进行一些测试,以使用 2 个数据帧从我的 python 脚本发送电子邮件:一个是原始的(即没有样式),另一个是带有样式的。对于样式,我遵循 pandas-style documentation
中的示例(输出 9)
我copy/pasted样式函数的定义如下:
def highlight_max(s):
'''
highlight the maximum in a Series yellow.
'''
is_max = s == s.max()
return ['background-color: yellow' if v else '' for v in is_max]
def color_negative_red(val):
"""
Takes a scalar and returns a string with
the css property `'color: red'` for negative
strings, black otherwise.
"""
color = 'red' if val < 0 else 'black'
return 'color: %s' % color
我为 运行 我的测试创建了一个虚拟数据框
np.random.seed(24)
df = pd.DataFrame({'A': np.linspace(1, 10, 10)})
df = pd.concat([df, pd.DataFrame(np.random.randn(10, 4), columns=list('BCDE'))],
axis=1)
df.iloc[3, 3] = np.nan
df.iloc[0, 2] = np.nan
这里是标准的 send_email 函数:
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
def send_email():
df1 = df.copy()
styler = df1.style.applymap(color_negative_red).apply(highlight_max).hide_index()
msg = MIMEMultipart()
msg['Subject'] = "This is a test"
msg['From'] = 'abc@dfe.com'
msg['To'] = 'abc@dfe.com'
html = """
<html>
<head></head>
<body>
<p>Hello!<br>
<br>
Raw:<br>
{0}
<br>
Formated<br>
{1}
</p>
</body>
</html>
""".format(df.to_html(), styler.render())
part1 = MIMEText(html, 'html')
msg.attach(part1)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(the_user, the_password)
server.send_message(msg)
server.quit()
我希望格式化的数据框显示时没有索引,最大行值以黄色突出显示,负数以红色突出显示。
这是我 sent/received:
发送的电子邮件的屏幕截图
所以格式化的数据框确实没有索引,但是黄色的最大高亮和红色的负数格式丢失了。我在 styler 变量中遗漏了什么吗?例如,我需要使用 .set_table_styles() 吗?如果可以,怎么做?
编辑:这里是 styler.render() 的输出,至少是它的顶部
<style type="text/css" >
#T_181a6_row0_col0,#T_181a6_row0_col1,(...){
color: black;
}
#T_181a6_row0_col3,#T_181a6_row0_col4,(...){
color: red;
}
#T_181a6_row2_col4,#T_181a6_row6_col2,(...){
color: black;
background-color: yellow;
}
</style><table id="T_181a6_" ><thead>
<tr>
<th class="col_heading level0 col0" >A</th>
<th class="col_heading level0 col1" >B</th>
<th class="col_heading level0 col2" >C</th>
<th class="col_heading level0 col3" >D</th>
<th class="col_heading level0 col4" >E</th>
</tr></thead><tbody>
<tr>
<td id="T_181a6_row0_col0" class="data row0 col0" >1.000000</td>
<td id="T_181a6_row0_col1" class="data row0 col1" >1.329212</td>
<td id="T_181a6_row0_col2" class="data row0 col2" >nan</td>
<td id="T_181a6_row0_col3" class="data row0 col3" >-0.316280</td>
<td id="T_181a6_row0_col4" class="data row0 col4" >-0.990810</td>
</tr>
(...)
</tbody></table>
Edit2:对 html 代码的格式感到抱歉,我不确定我该怎么做...
Edit3:当我将 styler.render() 的输出保存为 html 并在 google 中打开该文件时,例如数据帧格式正确。所以现在我想知道问题是否在于我如何将 styler.render() 包含到 html body (?) 我注意到我有 < body >.. .< / body > 包含在另一个 < body >...< / body > 中是正确的吗?
编辑 4:html 变量的输出保存为 html 并在 chrome 中打开:
所以问题是 webmail 正在剥离样式标题,它应该在 html 的每个 td 内。有没有办法在 render() 函数中做到这一点?
好的,感谢@Nathan 和@tripleee 指出核心问题。我已经安装了库 premailer,它允许转换 html 文档并将样式从 部分移动到 html 代码的相关部分。我现在可以在我的电子邮件中收到格式正确的 datfaram。
我提出的解决方案如下所示:
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
from premailer import transform
def send_email():
styler = (df_details.style.apply(highlight_same_week, axis=None).hide_index()).render()
msg = MIMEMultipart()
msg['Subject'] = "This is a test"
msg['From'] = 'abc@dfe.com'
msg['To'] = 'abc@dfe.com'
html = """
<html>
<head></head>
<body>
<p>Hello!<br>
<br>
Raw:<br>
{0}
<br>
Formated<br>
{1}
</p>
</body>
</html>
""".format(df.to_html(), styler)
html = transform(html)
part1 = MIMEText(html, 'html')
msg.attach(part1)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(the_user, the_password)
server.send_message(msg)
server.quit()
和 highlight_same_week 看起来像这样:
def highlight_same_week(s):
return pd.DataFrame(df_template.values, columns=s.columns)
所以 df_details 是具有我感兴趣的样式值的数据框,而 df_template 是与 df_details 具有相同尺寸的数据框,但它的值是样式元素,例如:
df_template.iloc[x, y] = 'background-color: #ffff00'
所以 fix/correct 的要点是:
- 使用正确的函数创建样式器变量(send_email 函数的第一行)
- 使用具有所需样式的数据框模板(这里我只用黄色高亮显示两列值在同一周内的行,基于 datetime.isocalendar())
- 使用预邮程序库中的转换函数转换我的原始 HTML 变量
我正在 运行 进行一些测试,以使用 2 个数据帧从我的 python 脚本发送电子邮件:一个是原始的(即没有样式),另一个是带有样式的。对于样式,我遵循 pandas-style documentation
中的示例(输出 9)我copy/pasted样式函数的定义如下:
def highlight_max(s):
'''
highlight the maximum in a Series yellow.
'''
is_max = s == s.max()
return ['background-color: yellow' if v else '' for v in is_max]
def color_negative_red(val):
"""
Takes a scalar and returns a string with
the css property `'color: red'` for negative
strings, black otherwise.
"""
color = 'red' if val < 0 else 'black'
return 'color: %s' % color
我为 运行 我的测试创建了一个虚拟数据框
np.random.seed(24)
df = pd.DataFrame({'A': np.linspace(1, 10, 10)})
df = pd.concat([df, pd.DataFrame(np.random.randn(10, 4), columns=list('BCDE'))],
axis=1)
df.iloc[3, 3] = np.nan
df.iloc[0, 2] = np.nan
这里是标准的 send_email 函数:
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
def send_email():
df1 = df.copy()
styler = df1.style.applymap(color_negative_red).apply(highlight_max).hide_index()
msg = MIMEMultipart()
msg['Subject'] = "This is a test"
msg['From'] = 'abc@dfe.com'
msg['To'] = 'abc@dfe.com'
html = """
<html>
<head></head>
<body>
<p>Hello!<br>
<br>
Raw:<br>
{0}
<br>
Formated<br>
{1}
</p>
</body>
</html>
""".format(df.to_html(), styler.render())
part1 = MIMEText(html, 'html')
msg.attach(part1)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(the_user, the_password)
server.send_message(msg)
server.quit()
我希望格式化的数据框显示时没有索引,最大行值以黄色突出显示,负数以红色突出显示。
这是我 sent/received:
发送的电子邮件的屏幕截图所以格式化的数据框确实没有索引,但是黄色的最大高亮和红色的负数格式丢失了。我在 styler 变量中遗漏了什么吗?例如,我需要使用 .set_table_styles() 吗?如果可以,怎么做?
编辑:这里是 styler.render() 的输出,至少是它的顶部
<style type="text/css" >
#T_181a6_row0_col0,#T_181a6_row0_col1,(...){
color: black;
}
#T_181a6_row0_col3,#T_181a6_row0_col4,(...){
color: red;
}
#T_181a6_row2_col4,#T_181a6_row6_col2,(...){
color: black;
background-color: yellow;
}
</style><table id="T_181a6_" ><thead>
<tr>
<th class="col_heading level0 col0" >A</th>
<th class="col_heading level0 col1" >B</th>
<th class="col_heading level0 col2" >C</th>
<th class="col_heading level0 col3" >D</th>
<th class="col_heading level0 col4" >E</th>
</tr></thead><tbody>
<tr>
<td id="T_181a6_row0_col0" class="data row0 col0" >1.000000</td>
<td id="T_181a6_row0_col1" class="data row0 col1" >1.329212</td>
<td id="T_181a6_row0_col2" class="data row0 col2" >nan</td>
<td id="T_181a6_row0_col3" class="data row0 col3" >-0.316280</td>
<td id="T_181a6_row0_col4" class="data row0 col4" >-0.990810</td>
</tr>
(...)
</tbody></table>
Edit2:对 html 代码的格式感到抱歉,我不确定我该怎么做...
Edit3:当我将 styler.render() 的输出保存为 html 并在 google 中打开该文件时,例如数据帧格式正确。所以现在我想知道问题是否在于我如何将 styler.render() 包含到 html body (?) 我注意到我有 < body >.. .< / body > 包含在另一个 < body >...< / body > 中是正确的吗?
编辑 4:html 变量的输出保存为 html 并在 chrome 中打开:
所以问题是 webmail 正在剥离样式标题,它应该在 html 的每个 td 内。有没有办法在 render() 函数中做到这一点?
好的,感谢@Nathan 和@tripleee 指出核心问题。我已经安装了库 premailer,它允许转换 html 文档并将样式从 部分移动到 html 代码的相关部分。我现在可以在我的电子邮件中收到格式正确的 datfaram。
我提出的解决方案如下所示:
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
from premailer import transform
def send_email():
styler = (df_details.style.apply(highlight_same_week, axis=None).hide_index()).render()
msg = MIMEMultipart()
msg['Subject'] = "This is a test"
msg['From'] = 'abc@dfe.com'
msg['To'] = 'abc@dfe.com'
html = """
<html>
<head></head>
<body>
<p>Hello!<br>
<br>
Raw:<br>
{0}
<br>
Formated<br>
{1}
</p>
</body>
</html>
""".format(df.to_html(), styler)
html = transform(html)
part1 = MIMEText(html, 'html')
msg.attach(part1)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(the_user, the_password)
server.send_message(msg)
server.quit()
和 highlight_same_week 看起来像这样:
def highlight_same_week(s):
return pd.DataFrame(df_template.values, columns=s.columns)
所以 df_details 是具有我感兴趣的样式值的数据框,而 df_template 是与 df_details 具有相同尺寸的数据框,但它的值是样式元素,例如:
df_template.iloc[x, y] = 'background-color: #ffff00'
所以 fix/correct 的要点是:
- 使用正确的函数创建样式器变量(send_email 函数的第一行)
- 使用具有所需样式的数据框模板(这里我只用黄色高亮显示两列值在同一周内的行,基于 datetime.isocalendar())
- 使用预邮程序库中的转换函数转换我的原始 HTML 变量