5

I am trying to return df.to_html() with one bold column. I have only tried

df = pd.DataFrame({'important_column': [1,2,3,4], 
                   'dummy_column': [5,6,7,8]})

def some_function()
      df.apply(lambda x: '<b>' + str(df['important_column']) + '</b>', axis=1)
      return [df.to_html()]

But it doesn't seem to work. Does any one know a practical solution?

Finrod Felagund
  • 1,231
  • 2
  • 14
  • 18

3 Answers3

8

You can use a df.style.set_properties and then .render() which'll prefix the normal table output from .to_html() with an appropriate style element. (note this does not physically wrap your text elements inside a <b> or <strong> or whatever tags you wish but purely provides styling for those cells - which may or may not be what you want depending on the use case)

html = df.style.set_properties(
    subset=['important_column'], 
    **{'font-weight': 'bold'}
).render()

(example shown in jupyter notebook)

enter image description here

Jon Clements
  • 138,671
  • 33
  • 247
  • 280
2

You forget assign output, but faster vectorized solution is convert column to string and add strings with no apply with f strings:

def some_function():

    df['important_column'] = [f'<b>{x}</b>' for x in df['important_column']]
    #alternative1 
    df['important_column'] =  '<b>' + df['important_column'].astype(str) + '</b>'
    #alternative2
    #df['important_column'] = df['important_column'].apply(lambda x: '<b>' + str(x) + '</b>')
    #alternative3, thanks @Jon Clements
    #df['important_column'] = df['important_column'].apply('<b>{}</b>?'.format)
    return df.to_html()

EDIT:

df['important_column'] = [f'<b>{x}</b>' for x in df['important_column']]
print (df.to_html(escape=False))
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>important_column</th>
      <th>dummy_column</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td><b>1</b></td>
      <td>5</td>
    </tr>
    <tr>
      <th>1</th>
      <td><b>2</b></td>
      <td>6</td>
    </tr>
    <tr>
      <th>2</th>
      <td><b>3</b></td>
      <td>7</td>
    </tr>
    <tr>
      <th>3</th>
      <td><b>4</b></td>
      <td>8</td>
    </tr>
  </tbody>
</table>

Timings:

df = pd.DataFrame({'important_column': [1,2,3,4], 
                   'dummy_column': [5,6,7,8]})

df = pd.concat([df] * 10000, ignore_index=True)

In [213]: %timeit df['important_column'] = [f'<b>{x}</b>' for x in df['important_column']]
74 ms ± 22.2 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [214]: %timeit df['important_column'] = df['important_column'].apply(lambda x: '<b>' + str(x) + '</b>')
150 ms ± 7.75 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [216]: %timeit df['important_column'].apply('<b>{}</b>?'.format)
133 ms ± 238 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [217]: %timeit '<b>' + df['important_column'].astype(str) + '</b>'
266 ms ± 1.21 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
jezrael
  • 822,522
  • 95
  • 1,334
  • 1,252
  • `df['important_column'].apply('{}?'.format)` should be fairly performant as well - it'll let the format handle the conversion to string and it makes the markup more readable and easier to adjust imho. – Jon Clements Aug 31 '18 at 08:02
  • @JonClements - You are right, just tested. `df = pd.concat([df] * 1000, ignore_index=True) In [198]: %timeit df['important_column'].apply('{}?'.format) In [199]: %timeit '' + df['important_column'].astype(str) + ''` – jezrael Aug 31 '18 at 08:06
  • `1.75 ms ± 278 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 2.6 ms ± 24.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)` – jezrael Aug 31 '18 at 08:06
0

It was my fault I didn't address the question properly, I need to have df.to_html() at the end of the function, because this html is displayed on a web-page, created with flask, so non of the above work for me.

I found a cheap work around, which suites my needs:

def some_function():
    df['important_column'] = '^' + df['important_column'].astype(str) + '+'
    return [df.to_html(classes='greenARTstyle').replace('^', '<b>').replace('+', '</b>')]
Finrod Felagund
  • 1,231
  • 2
  • 14
  • 18
  • 1
    Check my edit, need `print (df.to_html(escape=False))` – jezrael Aug 31 '18 at 08:09
  • Have you already got a style sheet in your flask then that you're using? If that's the case then it appears you could return `df.to_html(classes='important_table')` so you can identify it, then adjust your existing stylesheet to have `table.important_table td.col1 { font-weight: bold }`... – Jon Clements Aug 31 '18 at 08:11
  • Thanks @jezrael works! To Jon, I am quite unfamiliar with CSS, and I struggle with it. We are having few web-designers at our company, and the web-page I am creating is only for internal needs so ... yeah I need the easiest solutions from time to time – Finrod Felagund Aug 31 '18 at 08:12
  • 1
    That way - unless you really, really need to have `` tags around your cells (likely not if it's just for displaying in an app) - then you just let the browser render the bold for the column by styling... also means if you want to change the colour/size/alignment or background - you do that in one place without introducing more html (and worry about escaping)... – Jon Clements Aug 31 '18 at 08:16
  • @Finrod fine... just saying this actually isn't an easy way... also quite surprised using something like `{{ table|safe }}` in your jinja2 template and passing `html` from my answer below as `table` to your template didn't work :(. – Jon Clements Aug 31 '18 at 08:18