It looks like the float_format doesn't play nice with na_rep. However, you can work around it if you pass a function to float_format that conditionally handles your NaNs along with the float formatting you want:
>>> df Group Data 0 A 1.2225 1 A NaN
Reproducing your problem:
>>> out = StringIO() >>> df.to_html(out,na_rep="Ted",float_format='{0:.2f}'.format) >>> out.getvalue() <table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th>Group</th> <th>Data</th> </tr> </thead> <tbody> <tr> <th>0</th> <td> A</td> <td>1.22</td> </tr> <tr> <th>1</th> <td> A</td> <td> nan</td> </tr> </tbody>
So you get the proper float precision but not the correct na_rep. But the following seems to work:
>>> out = StringIO() >>> fmt = lambda x: '{0:.2f}'.format(x) if pd.notnull(x) else 'Ted' >>> df.to_html(out,float_format=fmt) >>> out.getvalue() <table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th>Group</th> <th>Data</th> </tr> </thead> <tbody> <tr> <th>0</th> <td> A</td> <td>1.22</td> </tr> <tr> <th>1</th> <td> A</td> <td> Ted</td> </tr> </tbody> </table>