在pandas中将特定的浮点数据帧列格式化为百分比

126

我正在尝试在IPython笔记本中写一篇论文,但在显示格式方面遇到了一些问题。假设我有以下数据框df,是否有任何方法可以将 var1 var2 格式化为两位小数,将 var3 格式化为百分比。

       var1        var2         var3    
id                                              
0    1.458315    1.500092   -0.005709   
1    1.576704    1.608445   -0.005122    
2    1.629253    1.652577   -0.004754    
3    1.669331    1.685456   -0.003525   
4    1.705139    1.712096   -0.003134   
5    1.740447    1.741961   -0.001223   
6    1.775980    1.770801   -0.001723    
7    1.812037    1.799327   -0.002013    
8    1.853130    1.822982   -0.001396    
9    1.943985    1.868401    0.005732

这里的数字未乘以100,例如 -0.0057=-0.57%。


1
如果有人在2014年之后看到这个问题,请查看我的答案,那里有一个简明的回答。 - BhishanPoudel
1
答案适用于即时格式化,但我希望能够“附加”格式到列上,这样我就可以继续对数据框进行其他操作,并且它始终以该格式打印该列(除非我将格式重置为其他内容)。这可行吗? - krubo
10个回答

204

接受的答案建议为了呈现的目的修改原始数据,这通常不是您想要的。想象一下,如果您需要使用这些列进行进一步分析,并且需要您在四舍五入时丢失的精度。

您可以修改数据框中单个列的格式,例如:

output = df.to_string(formatters={
    'var1': '{:,.2f}'.format,
    'var2': '{:,.2f}'.format,
    'var3': '{:,.2%}'.format
})
print(output)

提供信息,'{:,.2%}'.format(0.214)将产生21.40%的结果,因此无需乘以100。

您不再拥有漂亮的HTML表格,而是文本表示。如果需要保留HTML,请改用to_html函数。

from IPython.core.display import display, HTML
output = df.to_html(formatters={
    'var1': '{:,.2f}'.format,
    'var2': '{:,.2f}'.format,
    'var3': '{:,.2%}'.format
})
display(HTML(output))

更新

从pandas 0.17.1开始,使用起来更加简单,并且我们可以立即获得一个漂亮的HTML表格:

df.style.format({
    'var1': '{:,.2f}'.format,
    'var2': '{:,.2f}'.format,
    'var3': '{:,.2%}'.format,
})

1
如果您的数据框架中有 n 或可变数量的列,并且想要在所有列中应用相同的格式,但您可能不知道所有列标题,则无需将格式器放入字典中,可以使用列表并像这样创造性地完成:output = df.to_html(formatters=n * ['{:,.2%}'.format])。 - Afflatus
一个标准的这样的集合,使用字典和属性访问将会很棒。 - Wes Turner
3
不需要使用.format部分,可以省略它们。 - MarianD
7
df.style.format({'var3': '{:,.2%}'}) - 这段代码无法生效。数值保持不变,即没有显示百分号(%)。 - zwornik
3
@zwornik 在 '{:.2f}%' 中,% 需要放在括号外面。 - theFrok
显示剩余4条评论

66

您还可以设置浮点数的默认格式:

pd.options.display.float_format = '{:.2%}'.format

使用'{:.2%}'代替'{:.2f}%' - 前者可以将0.41正确转换为41.00%,后者会错误地转换为0.41%


2
很好了解,与OP有关在Python笔记本中输出的问题相关。 - Jim
3
如果百分比仍以小数形式给出(例如使用df.pct_change()时):pd.options.display.float_format = '{:.2%}'.format - Hugo Ideler
1
当然,这会影响到您所有的数据框,因此您必须再次取消设置以显示非百分比浮点数。 - fantabolous
1
根据@fantabolous的评论,这并不是真正有用的。 - GenDemo

49

使用round函数替换值,并格式化百分数数字的字符串表示:

df['var2'] = pd.Series([round(val, 2) for val in df['var2']], index = df.index)
df['var3'] = pd.Series(["{0:.2f}%".format(val * 100) for val in df['var3']], index = df.index)

round函数可以将浮点数四舍五入到函数提供的第二个参数指定的小数位数。

字符串格式化允许您按照您的意愿表示数字。您可以通过更改f前面的数字来更改显示的小数位数。

p.s. 我不确定您的“百分比”数字是否已经乘以了100。如果已经乘以,则需要更改显示的小数位数并删除百分之一百的乘法。


2
谢谢,这会改变每个列中的实际值吗? - user3576212
1
要将系列中的值四舍五入,您也可以使用 df['var2'].round(2) - Ben Southgate
4
你可以为浮点数设置默认格式:pd.options.display.float_format = '{:.2f}%'.format。 - Romain Jouin
1
@romain 非常好的建议(适用于某些用例),它应该是自己的答案(这样我就可以点赞了)。虽然它需要调整乘以100。 - Frames Catherine White
1
请参考下面的答案,它更好。 - Woody Pride
显示剩余3条评论

43

往往我们对计算出的完整有效数字感兴趣,但为了视觉美观,我们在显示数据框时可能只想看到几个小数点。

在jupyter-notebook中,pandas可以利用称为 style 的方法来利用HTML格式。

如果只想查看某些列的两个有效数字,则可以使用以下代码片段:

给定数据框

import numpy as np
import pandas as pd

df = pd.DataFrame({'var1': [1.458315, 1.576704, 1.629253, 1.6693310000000001, 1.705139, 1.740447, 1.77598, 1.812037, 1.85313, 1.9439849999999999],
          'var2': [1.500092, 1.6084450000000001, 1.652577, 1.685456, 1.7120959999999998, 1.741961, 1.7708009999999998, 1.7993270000000001, 1.8229819999999999, 1.8684009999999998],
          'var3': [-0.0057090000000000005, -0.005122, -0.0047539999999999995, -0.003525, -0.003134, -0.0012230000000000001, -0.0017230000000000001, -0.002013, -0.001396, 0.005732]})

print(df)
       var1      var2      var3
0  1.458315  1.500092 -0.005709
1  1.576704  1.608445 -0.005122
2  1.629253  1.652577 -0.004754
3  1.669331  1.685456 -0.003525
4  1.705139  1.712096 -0.003134
5  1.740447  1.741961 -0.001223
6  1.775980  1.770801 -0.001723
7  1.812037  1.799327 -0.002013
8  1.853130  1.822982 -0.001396
9  1.943985  1.868401  0.005732

获取所需格式的样式

    df.style.format({'var1': "{:.2f}",'var2': "{:.2f}",'var3': "{:.2%}"})

给予:

     var1   var2    var3
id          
0   1.46    1.50    -0.57%
1   1.58    1.61    -0.51%
2   1.63    1.65    -0.48%
3   1.67    1.69    -0.35%
4   1.71    1.71    -0.31%
5   1.74    1.74    -0.12%
6   1.78    1.77    -0.17%
7   1.81    1.80    -0.20%
8   1.85    1.82    -0.14%
9   1.94    1.87    0.57%

更新

如果找不到显示命令,请尝试以下操作:

from IPython.display import display

df_style = df.style.format({'var1': "{:.2f}",'var2': "{:.2f}",'var3': "{:.2%}"})

display(df_style)

要求

  • 使用display命令,需要在您的计算机上安装Ipython。
  • display命令不能在没有安装IPyton的在线Python解释器中使用,例如https://repl.it/languages/python3
  • display命令可以在jupyter-notebook、jupyter-lab、Google-colab、kaggle-kernels、IBM-watson、Mode-Analytics和许多其他平台上直接使用,您甚至无需从IPython.display导入它。

这是最符合Python风格的答案。 - FuzzyDuck
1
这个答案比被采纳的那个好多了。改变格式比实际改变底层值要好得多。 - philippjfr
1
@Poudel 这并不起作用。我使用了与你完全相同的代码,但var3没有被格式化为百分比。 - zwornik
1
@zwornik 尝试使用 display(df.style.format({'var1': "{:.2f}",'var2': "{:.2f}",'var3': "{:.2%}"})) - BhishanPoudel
事实上,当我使用这个答案时,我收到了以下消息: AttributeError: 'Styler' object has no attribute 'head' - fransua
显示剩余3条评论

28

根据@linqu的建议,您不应更改数据以用于演示。自pandas 0.17.1以来,(条件)格式化变得更加容易。引用文档

您可以通过使用DataFrame.style属性将条件格式化应用于DataFrame的可视化样式,该样式取决于其中的数据。这是一个返回pandas.Styler对象的属性,该对象具有有用的方法,用于格式化和显示DataFrames

对于您的示例,可以这样做(通常的表格将在Jupyter中显示):

df.style.format({
    'var1': '{:,.2f}'.format,
    'var2': '{:,.2f}'.format,
    'var3': '{:,.2%}'.format,
})

4
这个不起作用,我使用了与你完全相同的代码。 - zwornik
1
这将生成HTML输出,并对笔记本非常有用。 对于终端输出、打印到文件等,使用to_string方法是很好的选择。它需要Pandas版本1.5或更高版本。 - LudvigH

18

如果你需要在更大的一列范围内进行操作,可以采用另一种方法来完成

使用applymap函数

df[['var1','var2']] = df[['var1','var2']].applymap("{0:.2f}".format)
df['var3'] = df['var3'].applymap(lambda x: "{0:.2f}%".format(x*100))

如果您需要在多个列上应用函数,则applymap非常有用;对于此特定示例,它实质上是以下内容的缩写:

df[['var1','var2']].apply(lambda x: map(lambda x:'{:.2f}%'.format(x),x),axis=1)

下面是关于 Pandas 中 map、applymap 和 apply 方法的优秀解释:

Pandas 中 map、applymap 和 apply 方法之间的区别


8
作为一个类似于被接受答案的方法,可能会被认为更易读、优雅和通用(个人见解),您可以利用map方法:
# OP example
df['var3'].map(lambda n: '{:,.2%}'.format(n))

# also works on a series
series_example.map(lambda n: '{:,.2%}'.format(n))

就性能而言,这个解决方案与 OP 的解决方案非常接近(略微慢一些)。

另外,如果您选择使用 pd.options.display.float_format 方法,请考虑使用上下文管理器来处理状态,可以参考这个并行 numpy 示例


6

style.format是向量化的,因此我们可以直接将其应用于整个df(或仅适用于其数值列):

df[num_cols].style.format('{:,.3f}%')


请注意,如果df只包含一列,并且实际上是一个Series对象,那么首先需要将其转换为pandas DataFrame,例如使用pd.DataFrame(df[num_col]).style.format,或者如下所示:df[num_col].to_frame().style.format)。

2
应首先将该系列转换为数据框:df[num_cols].to_frame().style.format('{:,.3f}%')。 - Sahar

0

列表推导式有一个确定的结果,我已经成功地使用它了。 我认为你可以像下面这样使用Python列表推导式:

df['var1'] = ["{:.2f}".format(i) for i in df['var1'] ]
df['var2'] = ["{:.2f}".format(i) for i in df['var2'] ]
df['var3'] = ["{:.2%}".format(i) for i in df['var3'] ]

谢谢


0

这个答案 的基础上,我使用了apply函数来操作给定的序列。在我的情况下,我想要显示带有百分号格式的系列值计数。

我执行了以下操作:

df['my_col'].value_counts(normalize=True).apply(lambda x: "{0:.2f}%".format(x*100))
# Incident             88.16%
# StreetWorks          3.29% 
# Accident             2.36%
# ... 

不仅仅是

df['my_col'].value_counts(normalize=True)
# Incident             0.881634
# StreetWorks          0.032856
# Accident             0.023589
# ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接