PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/pandas/tests/formats/test_style.py

http://github.com/wesm/pandas
Python | 747 lines | 701 code | 34 blank | 12 comment | 8 complexity | f53c8cbdef2e4fb82bea4ba2088f38ea MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. import os
  2. from nose import SkipTest
  3. import copy
  4. import numpy as np
  5. import pandas as pd
  6. from pandas import DataFrame
  7. from pandas.util.testing import TestCase
  8. import pandas.util.testing as tm
  9. # Getting failures on a python 2.7 build with
  10. # whenever we try to import jinja, whether it's installed or not.
  11. # so we're explicitly skipping that one *before* we try to import
  12. # jinja. We still need to export the imports as globals,
  13. # since importing Styler tries to import jinja2.
  14. job_name = os.environ.get('JOB_NAME', None)
  15. if job_name == '27_slow_nnet_LOCALE':
  16. raise SkipTest("No jinja")
  17. try:
  18. # Do try except on just jinja, so the only reason
  19. # We skip is if jinja can't import, not something else
  20. import jinja2 # noqa
  21. except ImportError:
  22. raise SkipTest("No Jinja2")
  23. from pandas.formats.style import Styler, _get_level_lengths # noqa
  24. class TestStyler(TestCase):
  25. def setUp(self):
  26. np.random.seed(24)
  27. self.s = DataFrame({'A': np.random.permutation(range(6))})
  28. self.df = DataFrame({'A': [0, 1], 'B': np.random.randn(2)})
  29. self.f = lambda x: x
  30. self.g = lambda x: x
  31. def h(x, foo='bar'):
  32. return pd.Series(['color: %s' % foo], index=x.index, name=x.name)
  33. self.h = h
  34. self.styler = Styler(self.df)
  35. self.attrs = pd.DataFrame({'A': ['color: red', 'color: blue']})
  36. self.dataframes = [
  37. self.df,
  38. pd.DataFrame({'f': [1., 2.], 'o': ['a', 'b'],
  39. 'c': pd.Categorical(['a', 'b'])})
  40. ]
  41. def test_init_non_pandas(self):
  42. with tm.assertRaises(TypeError):
  43. Styler([1, 2, 3])
  44. def test_init_series(self):
  45. result = Styler(pd.Series([1, 2]))
  46. self.assertEqual(result.data.ndim, 2)
  47. def test_repr_html_ok(self):
  48. self.styler._repr_html_()
  49. def test_update_ctx(self):
  50. self.styler._update_ctx(self.attrs)
  51. expected = {(0, 0): ['color: red'],
  52. (1, 0): ['color: blue']}
  53. self.assertEqual(self.styler.ctx, expected)
  54. def test_update_ctx_flatten_multi(self):
  55. attrs = DataFrame({"A": ['color: red; foo: bar',
  56. 'color: blue; foo: baz']})
  57. self.styler._update_ctx(attrs)
  58. expected = {(0, 0): ['color: red', ' foo: bar'],
  59. (1, 0): ['color: blue', ' foo: baz']}
  60. self.assertEqual(self.styler.ctx, expected)
  61. def test_update_ctx_flatten_multi_traliing_semi(self):
  62. attrs = DataFrame({"A": ['color: red; foo: bar;',
  63. 'color: blue; foo: baz;']})
  64. self.styler._update_ctx(attrs)
  65. expected = {(0, 0): ['color: red', ' foo: bar'],
  66. (1, 0): ['color: blue', ' foo: baz']}
  67. self.assertEqual(self.styler.ctx, expected)
  68. def test_copy(self):
  69. s2 = copy.copy(self.styler)
  70. self.assertTrue(self.styler is not s2)
  71. self.assertTrue(self.styler.ctx is s2.ctx) # shallow
  72. self.assertTrue(self.styler._todo is s2._todo)
  73. self.styler._update_ctx(self.attrs)
  74. self.styler.highlight_max()
  75. self.assertEqual(self.styler.ctx, s2.ctx)
  76. self.assertEqual(self.styler._todo, s2._todo)
  77. def test_deepcopy(self):
  78. s2 = copy.deepcopy(self.styler)
  79. self.assertTrue(self.styler is not s2)
  80. self.assertTrue(self.styler.ctx is not s2.ctx)
  81. self.assertTrue(self.styler._todo is not s2._todo)
  82. self.styler._update_ctx(self.attrs)
  83. self.styler.highlight_max()
  84. self.assertNotEqual(self.styler.ctx, s2.ctx)
  85. self.assertEqual(s2._todo, [])
  86. self.assertNotEqual(self.styler._todo, s2._todo)
  87. def test_clear(self):
  88. s = self.df.style.highlight_max()._compute()
  89. self.assertTrue(len(s.ctx) > 0)
  90. self.assertTrue(len(s._todo) > 0)
  91. s.clear()
  92. self.assertTrue(len(s.ctx) == 0)
  93. self.assertTrue(len(s._todo) == 0)
  94. def test_render(self):
  95. df = pd.DataFrame({"A": [0, 1]})
  96. style = lambda x: pd.Series(["color: red", "color: blue"], name=x.name)
  97. s = Styler(df, uuid='AB').apply(style)
  98. s.render()
  99. # it worked?
  100. def test_render_double(self):
  101. df = pd.DataFrame({"A": [0, 1]})
  102. style = lambda x: pd.Series(["color: red; border: 1px",
  103. "color: blue; border: 2px"], name=x.name)
  104. s = Styler(df, uuid='AB').apply(style)
  105. s.render()
  106. # it worked?
  107. def test_set_properties(self):
  108. df = pd.DataFrame({"A": [0, 1]})
  109. result = df.style.set_properties(color='white',
  110. size='10px')._compute().ctx
  111. # order is deterministic
  112. v = ["color: white", "size: 10px"]
  113. expected = {(0, 0): v, (1, 0): v}
  114. self.assertEqual(result.keys(), expected.keys())
  115. for v1, v2 in zip(result.values(), expected.values()):
  116. self.assertEqual(sorted(v1), sorted(v2))
  117. def test_set_properties_subset(self):
  118. df = pd.DataFrame({'A': [0, 1]})
  119. result = df.style.set_properties(subset=pd.IndexSlice[0, 'A'],
  120. color='white')._compute().ctx
  121. expected = {(0, 0): ['color: white']}
  122. self.assertEqual(result, expected)
  123. def test_empty_index_name_doesnt_display(self):
  124. # https://github.com/pydata/pandas/pull/12090#issuecomment-180695902
  125. df = pd.DataFrame({'A': [1, 2], 'B': [3, 4], 'C': [5, 6]})
  126. result = df.style._translate()
  127. expected = [[{'class': 'blank level0', 'type': 'th', 'value': '',
  128. 'is_visible': True, 'display_value': ''},
  129. {'class': 'col_heading level0 col0',
  130. 'display_value': 'A',
  131. 'type': 'th',
  132. 'value': 'A',
  133. 'is_visible': True,
  134. 'attributes': ["colspan=1"],
  135. },
  136. {'class': 'col_heading level0 col1',
  137. 'display_value': 'B',
  138. 'type': 'th',
  139. 'value': 'B',
  140. 'is_visible': True,
  141. 'attributes': ["colspan=1"],
  142. },
  143. {'class': 'col_heading level0 col2',
  144. 'display_value': 'C',
  145. 'type': 'th',
  146. 'value': 'C',
  147. 'is_visible': True,
  148. 'attributes': ["colspan=1"],
  149. }]]
  150. self.assertEqual(result['head'], expected)
  151. def test_index_name(self):
  152. # https://github.com/pydata/pandas/issues/11655
  153. df = pd.DataFrame({'A': [1, 2], 'B': [3, 4], 'C': [5, 6]})
  154. result = df.set_index('A').style._translate()
  155. expected = [[{'class': 'blank level0', 'type': 'th', 'value': '',
  156. 'display_value': '', 'is_visible': True},
  157. {'class': 'col_heading level0 col0', 'type': 'th',
  158. 'value': 'B', 'display_value': 'B',
  159. 'is_visible': True, 'attributes': ['colspan=1']},
  160. {'class': 'col_heading level0 col1', 'type': 'th',
  161. 'value': 'C', 'display_value': 'C',
  162. 'is_visible': True, 'attributes': ['colspan=1']}],
  163. [{'class': 'index_name level0', 'type': 'th',
  164. 'value': 'A'},
  165. {'class': 'blank', 'type': 'th', 'value': ''},
  166. {'class': 'blank', 'type': 'th', 'value': ''}]]
  167. self.assertEqual(result['head'], expected)
  168. def test_multiindex_name(self):
  169. # https://github.com/pydata/pandas/issues/11655
  170. df = pd.DataFrame({'A': [1, 2], 'B': [3, 4], 'C': [5, 6]})
  171. result = df.set_index(['A', 'B']).style._translate()
  172. expected = [[
  173. {'class': 'blank', 'type': 'th', 'value': '',
  174. 'display_value': '', 'is_visible': True},
  175. {'class': 'blank level0', 'type': 'th', 'value': '',
  176. 'display_value': '', 'is_visible': True},
  177. {'class': 'col_heading level0 col0', 'type': 'th',
  178. 'value': 'C', 'display_value': 'C',
  179. 'is_visible': True, 'attributes': ['colspan=1'],
  180. }],
  181. [{'class': 'index_name level0', 'type': 'th',
  182. 'value': 'A'},
  183. {'class': 'index_name level1', 'type': 'th',
  184. 'value': 'B'},
  185. {'class': 'blank', 'type': 'th', 'value': ''}]]
  186. self.assertEqual(result['head'], expected)
  187. def test_numeric_columns(self):
  188. # https://github.com/pydata/pandas/issues/12125
  189. # smoke test for _translate
  190. df = pd.DataFrame({0: [1, 2, 3]})
  191. df.style._translate()
  192. def test_apply_axis(self):
  193. df = pd.DataFrame({'A': [0, 0], 'B': [1, 1]})
  194. f = lambda x: ['val: %s' % x.max() for v in x]
  195. result = df.style.apply(f, axis=1)
  196. self.assertEqual(len(result._todo), 1)
  197. self.assertEqual(len(result.ctx), 0)
  198. result._compute()
  199. expected = {(0, 0): ['val: 1'], (0, 1): ['val: 1'],
  200. (1, 0): ['val: 1'], (1, 1): ['val: 1']}
  201. self.assertEqual(result.ctx, expected)
  202. result = df.style.apply(f, axis=0)
  203. expected = {(0, 0): ['val: 0'], (0, 1): ['val: 1'],
  204. (1, 0): ['val: 0'], (1, 1): ['val: 1']}
  205. result._compute()
  206. self.assertEqual(result.ctx, expected)
  207. result = df.style.apply(f) # default
  208. result._compute()
  209. self.assertEqual(result.ctx, expected)
  210. def test_apply_subset(self):
  211. axes = [0, 1]
  212. slices = [pd.IndexSlice[:], pd.IndexSlice[:, ['A']],
  213. pd.IndexSlice[[1], :], pd.IndexSlice[[1], ['A']],
  214. pd.IndexSlice[:2, ['A', 'B']]]
  215. for ax in axes:
  216. for slice_ in slices:
  217. result = self.df.style.apply(self.h, axis=ax, subset=slice_,
  218. foo='baz')._compute().ctx
  219. expected = dict(((r, c), ['color: baz'])
  220. for r, row in enumerate(self.df.index)
  221. for c, col in enumerate(self.df.columns)
  222. if row in self.df.loc[slice_].index and
  223. col in self.df.loc[slice_].columns)
  224. self.assertEqual(result, expected)
  225. def test_applymap_subset(self):
  226. def f(x):
  227. return 'foo: bar'
  228. slices = [pd.IndexSlice[:], pd.IndexSlice[:, ['A']],
  229. pd.IndexSlice[[1], :], pd.IndexSlice[[1], ['A']],
  230. pd.IndexSlice[:2, ['A', 'B']]]
  231. for slice_ in slices:
  232. result = self.df.style.applymap(f, subset=slice_)._compute().ctx
  233. expected = dict(((r, c), ['foo: bar'])
  234. for r, row in enumerate(self.df.index)
  235. for c, col in enumerate(self.df.columns)
  236. if row in self.df.loc[slice_].index and
  237. col in self.df.loc[slice_].columns)
  238. self.assertEqual(result, expected)
  239. def test_empty(self):
  240. df = pd.DataFrame({'A': [1, 0]})
  241. s = df.style
  242. s.ctx = {(0, 0): ['color: red'],
  243. (1, 0): ['']}
  244. result = s._translate()['cellstyle']
  245. expected = [{'props': [['color', ' red']], 'selector': 'row0_col0'},
  246. {'props': [['', '']], 'selector': 'row1_col0'}]
  247. self.assertEqual(result, expected)
  248. def test_bar(self):
  249. df = pd.DataFrame({'A': [0, 1, 2]})
  250. result = df.style.bar()._compute().ctx
  251. expected = {
  252. (0, 0): ['width: 10em', ' height: 80%'],
  253. (1, 0): ['width: 10em', ' height: 80%',
  254. 'background: linear-gradient('
  255. '90deg,#d65f5f 50.0%, transparent 0%)'],
  256. (2, 0): ['width: 10em', ' height: 80%',
  257. 'background: linear-gradient('
  258. '90deg,#d65f5f 100.0%, transparent 0%)']
  259. }
  260. self.assertEqual(result, expected)
  261. result = df.style.bar(color='red', width=50)._compute().ctx
  262. expected = {
  263. (0, 0): ['width: 10em', ' height: 80%'],
  264. (1, 0): ['width: 10em', ' height: 80%',
  265. 'background: linear-gradient('
  266. '90deg,red 25.0%, transparent 0%)'],
  267. (2, 0): ['width: 10em', ' height: 80%',
  268. 'background: linear-gradient('
  269. '90deg,red 50.0%, transparent 0%)']
  270. }
  271. self.assertEqual(result, expected)
  272. df['C'] = ['a'] * len(df)
  273. result = df.style.bar(color='red', width=50)._compute().ctx
  274. self.assertEqual(result, expected)
  275. df['C'] = df['C'].astype('category')
  276. result = df.style.bar(color='red', width=50)._compute().ctx
  277. self.assertEqual(result, expected)
  278. def test_bar_0points(self):
  279. df = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
  280. result = df.style.bar()._compute().ctx
  281. expected = {(0, 0): ['width: 10em', ' height: 80%'],
  282. (0, 1): ['width: 10em', ' height: 80%'],
  283. (0, 2): ['width: 10em', ' height: 80%'],
  284. (1, 0): ['width: 10em', ' height: 80%',
  285. 'background: linear-gradient(90deg,#d65f5f 50.0%,'
  286. ' transparent 0%)'],
  287. (1, 1): ['width: 10em', ' height: 80%',
  288. 'background: linear-gradient(90deg,#d65f5f 50.0%,'
  289. ' transparent 0%)'],
  290. (1, 2): ['width: 10em', ' height: 80%',
  291. 'background: linear-gradient(90deg,#d65f5f 50.0%,'
  292. ' transparent 0%)'],
  293. (2, 0): ['width: 10em', ' height: 80%',
  294. 'background: linear-gradient(90deg,#d65f5f 100.0%'
  295. ', transparent 0%)'],
  296. (2, 1): ['width: 10em', ' height: 80%',
  297. 'background: linear-gradient(90deg,#d65f5f 100.0%'
  298. ', transparent 0%)'],
  299. (2, 2): ['width: 10em', ' height: 80%',
  300. 'background: linear-gradient(90deg,#d65f5f 100.0%'
  301. ', transparent 0%)']}
  302. self.assertEqual(result, expected)
  303. result = df.style.bar(axis=1)._compute().ctx
  304. expected = {(0, 0): ['width: 10em', ' height: 80%'],
  305. (0, 1): ['width: 10em', ' height: 80%',
  306. 'background: linear-gradient(90deg,#d65f5f 50.0%,'
  307. ' transparent 0%)'],
  308. (0, 2): ['width: 10em', ' height: 80%',
  309. 'background: linear-gradient(90deg,#d65f5f 100.0%'
  310. ', transparent 0%)'],
  311. (1, 0): ['width: 10em', ' height: 80%'],
  312. (1, 1): ['width: 10em', ' height: 80%',
  313. 'background: linear-gradient(90deg,#d65f5f 50.0%'
  314. ', transparent 0%)'],
  315. (1, 2): ['width: 10em', ' height: 80%',
  316. 'background: linear-gradient(90deg,#d65f5f 100.0%'
  317. ', transparent 0%)'],
  318. (2, 0): ['width: 10em', ' height: 80%'],
  319. (2, 1): ['width: 10em', ' height: 80%',
  320. 'background: linear-gradient(90deg,#d65f5f 50.0%'
  321. ', transparent 0%)'],
  322. (2, 2): ['width: 10em', ' height: 80%',
  323. 'background: linear-gradient(90deg,#d65f5f 100.0%'
  324. ', transparent 0%)']}
  325. self.assertEqual(result, expected)
  326. def test_highlight_null(self, null_color='red'):
  327. df = pd.DataFrame({'A': [0, np.nan]})
  328. result = df.style.highlight_null()._compute().ctx
  329. expected = {(0, 0): [''],
  330. (1, 0): ['background-color: red']}
  331. self.assertEqual(result, expected)
  332. def test_nonunique_raises(self):
  333. df = pd.DataFrame([[1, 2]], columns=['A', 'A'])
  334. with tm.assertRaises(ValueError):
  335. df.style
  336. with tm.assertRaises(ValueError):
  337. Styler(df)
  338. def test_caption(self):
  339. styler = Styler(self.df, caption='foo')
  340. result = styler.render()
  341. self.assertTrue(all(['caption' in result, 'foo' in result]))
  342. styler = self.df.style
  343. result = styler.set_caption('baz')
  344. self.assertTrue(styler is result)
  345. self.assertEqual(styler.caption, 'baz')
  346. def test_uuid(self):
  347. styler = Styler(self.df, uuid='abc123')
  348. result = styler.render()
  349. self.assertTrue('abc123' in result)
  350. styler = self.df.style
  351. result = styler.set_uuid('aaa')
  352. self.assertTrue(result is styler)
  353. self.assertEqual(result.uuid, 'aaa')
  354. def test_table_styles(self):
  355. style = [{'selector': 'th', 'props': [('foo', 'bar')]}]
  356. styler = Styler(self.df, table_styles=style)
  357. result = ' '.join(styler.render().split())
  358. self.assertTrue('th { foo: bar; }' in result)
  359. styler = self.df.style
  360. result = styler.set_table_styles(style)
  361. self.assertTrue(styler is result)
  362. self.assertEqual(styler.table_styles, style)
  363. def test_table_attributes(self):
  364. attributes = 'class="foo" data-bar'
  365. styler = Styler(self.df, table_attributes=attributes)
  366. result = styler.render()
  367. self.assertTrue('class="foo" data-bar' in result)
  368. result = self.df.style.set_table_attributes(attributes).render()
  369. self.assertTrue('class="foo" data-bar' in result)
  370. def test_precision(self):
  371. with pd.option_context('display.precision', 10):
  372. s = Styler(self.df)
  373. self.assertEqual(s.precision, 10)
  374. s = Styler(self.df, precision=2)
  375. self.assertEqual(s.precision, 2)
  376. s2 = s.set_precision(4)
  377. self.assertTrue(s is s2)
  378. self.assertEqual(s.precision, 4)
  379. def test_apply_none(self):
  380. def f(x):
  381. return pd.DataFrame(np.where(x == x.max(), 'color: red', ''),
  382. index=x.index, columns=x.columns)
  383. result = (pd.DataFrame([[1, 2], [3, 4]])
  384. .style.apply(f, axis=None)._compute().ctx)
  385. self.assertEqual(result[(1, 1)], ['color: red'])
  386. def test_trim(self):
  387. result = self.df.style.render() # trim=True
  388. self.assertEqual(result.count('#'), 0)
  389. result = self.df.style.highlight_max().render()
  390. self.assertEqual(result.count('#'), len(self.df.columns))
  391. def test_highlight_max(self):
  392. df = pd.DataFrame([[1, 2], [3, 4]], columns=['A', 'B'])
  393. # max(df) = min(-df)
  394. for max_ in [True, False]:
  395. if max_:
  396. attr = 'highlight_max'
  397. else:
  398. df = -df
  399. attr = 'highlight_min'
  400. result = getattr(df.style, attr)()._compute().ctx
  401. self.assertEqual(result[(1, 1)], ['background-color: yellow'])
  402. result = getattr(df.style, attr)(color='green')._compute().ctx
  403. self.assertEqual(result[(1, 1)], ['background-color: green'])
  404. result = getattr(df.style, attr)(subset='A')._compute().ctx
  405. self.assertEqual(result[(1, 0)], ['background-color: yellow'])
  406. result = getattr(df.style, attr)(axis=0)._compute().ctx
  407. expected = {(1, 0): ['background-color: yellow'],
  408. (1, 1): ['background-color: yellow'],
  409. (0, 1): [''], (0, 0): ['']}
  410. self.assertEqual(result, expected)
  411. result = getattr(df.style, attr)(axis=1)._compute().ctx
  412. expected = {(0, 1): ['background-color: yellow'],
  413. (1, 1): ['background-color: yellow'],
  414. (0, 0): [''], (1, 0): ['']}
  415. self.assertEqual(result, expected)
  416. # separate since we cant negate the strs
  417. df['C'] = ['a', 'b']
  418. result = df.style.highlight_max()._compute().ctx
  419. expected = {(1, 1): ['background-color: yellow']}
  420. result = df.style.highlight_min()._compute().ctx
  421. expected = {(0, 0): ['background-color: yellow']}
  422. def test_export(self):
  423. f = lambda x: 'color: red' if x > 0 else 'color: blue'
  424. g = lambda x, y, z: 'color: %s' if x > 0 else 'color: %s' % z
  425. style1 = self.styler
  426. style1.applymap(f)\
  427. .applymap(g, y='a', z='b')\
  428. .highlight_max()
  429. result = style1.export()
  430. style2 = self.df.style
  431. style2.use(result)
  432. self.assertEqual(style1._todo, style2._todo)
  433. style2.render()
  434. def test_display_format(self):
  435. df = pd.DataFrame(np.random.random(size=(2, 2)))
  436. ctx = df.style.format("{:0.1f}")._translate()
  437. self.assertTrue(all(['display_value' in c for c in row]
  438. for row in ctx['body']))
  439. self.assertTrue(all([len(c['display_value']) <= 3 for c in row[1:]]
  440. for row in ctx['body']))
  441. self.assertTrue(
  442. len(ctx['body'][0][1]['display_value'].lstrip('-')) <= 3)
  443. def test_display_format_raises(self):
  444. df = pd.DataFrame(np.random.randn(2, 2))
  445. with tm.assertRaises(TypeError):
  446. df.style.format(5)
  447. with tm.assertRaises(TypeError):
  448. df.style.format(True)
  449. def test_display_subset(self):
  450. df = pd.DataFrame([[.1234, .1234], [1.1234, 1.1234]],
  451. columns=['a', 'b'])
  452. ctx = df.style.format({"a": "{:0.1f}", "b": "{0:.2%}"},
  453. subset=pd.IndexSlice[0, :])._translate()
  454. expected = '0.1'
  455. self.assertEqual(ctx['body'][0][1]['display_value'], expected)
  456. self.assertEqual(ctx['body'][1][1]['display_value'], '1.1234')
  457. self.assertEqual(ctx['body'][0][2]['display_value'], '12.34%')
  458. raw_11 = '1.1234'
  459. ctx = df.style.format("{:0.1f}",
  460. subset=pd.IndexSlice[0, :])._translate()
  461. self.assertEqual(ctx['body'][0][1]['display_value'], expected)
  462. self.assertEqual(ctx['body'][1][1]['display_value'], raw_11)
  463. ctx = df.style.format("{:0.1f}",
  464. subset=pd.IndexSlice[0, :])._translate()
  465. self.assertEqual(ctx['body'][0][1]['display_value'], expected)
  466. self.assertEqual(ctx['body'][1][1]['display_value'], raw_11)
  467. ctx = df.style.format("{:0.1f}",
  468. subset=pd.IndexSlice['a'])._translate()
  469. self.assertEqual(ctx['body'][0][1]['display_value'], expected)
  470. self.assertEqual(ctx['body'][0][2]['display_value'], '0.1234')
  471. ctx = df.style.format("{:0.1f}",
  472. subset=pd.IndexSlice[0, 'a'])._translate()
  473. self.assertEqual(ctx['body'][0][1]['display_value'], expected)
  474. self.assertEqual(ctx['body'][1][1]['display_value'], raw_11)
  475. ctx = df.style.format("{:0.1f}",
  476. subset=pd.IndexSlice[[0, 1], ['a']])._translate()
  477. self.assertEqual(ctx['body'][0][1]['display_value'], expected)
  478. self.assertEqual(ctx['body'][1][1]['display_value'], '1.1')
  479. self.assertEqual(ctx['body'][0][2]['display_value'], '0.1234')
  480. self.assertEqual(ctx['body'][1][2]['display_value'], '1.1234')
  481. def test_display_dict(self):
  482. df = pd.DataFrame([[.1234, .1234], [1.1234, 1.1234]],
  483. columns=['a', 'b'])
  484. ctx = df.style.format({"a": "{:0.1f}", "b": "{0:.2%}"})._translate()
  485. self.assertEqual(ctx['body'][0][1]['display_value'], '0.1')
  486. self.assertEqual(ctx['body'][0][2]['display_value'], '12.34%')
  487. df['c'] = ['aaa', 'bbb']
  488. ctx = df.style.format({"a": "{:0.1f}", "c": str.upper})._translate()
  489. self.assertEqual(ctx['body'][0][1]['display_value'], '0.1')
  490. self.assertEqual(ctx['body'][0][3]['display_value'], 'AAA')
  491. def test_bad_apply_shape(self):
  492. df = pd.DataFrame([[1, 2], [3, 4]])
  493. with tm.assertRaises(ValueError):
  494. df.style._apply(lambda x: 'x', subset=pd.IndexSlice[[0, 1], :])
  495. with tm.assertRaises(ValueError):
  496. df.style._apply(lambda x: [''], subset=pd.IndexSlice[[0, 1], :])
  497. with tm.assertRaises(ValueError):
  498. df.style._apply(lambda x: ['', '', '', ''])
  499. with tm.assertRaises(ValueError):
  500. df.style._apply(lambda x: ['', '', ''], subset=1)
  501. with tm.assertRaises(ValueError):
  502. df.style._apply(lambda x: ['', '', ''], axis=1)
  503. def test_apply_bad_return(self):
  504. def f(x):
  505. return ''
  506. df = pd.DataFrame([[1, 2], [3, 4]])
  507. with tm.assertRaises(TypeError):
  508. df.style._apply(f, axis=None)
  509. def test_apply_bad_labels(self):
  510. def f(x):
  511. return pd.DataFrame(index=[1, 2], columns=['a', 'b'])
  512. df = pd.DataFrame([[1, 2], [3, 4]])
  513. with tm.assertRaises(ValueError):
  514. df.style._apply(f, axis=None)
  515. def test_get_level_lengths(self):
  516. index = pd.MultiIndex.from_product([['a', 'b'], [0, 1, 2]])
  517. expected = {(0, 0): 3, (0, 3): 3, (1, 0): 1, (1, 1): 1, (1, 2): 1,
  518. (1, 3): 1, (1, 4): 1, (1, 5): 1}
  519. result = _get_level_lengths(index)
  520. tm.assert_dict_equal(result, expected)
  521. def test_get_level_lengths_un_sorted(self):
  522. index = pd.MultiIndex.from_arrays([
  523. [1, 1, 2, 1],
  524. ['a', 'b', 'b', 'd']
  525. ])
  526. expected = {(0, 0): 2, (0, 2): 1, (0, 3): 1,
  527. (1, 0): 1, (1, 1): 1, (1, 2): 1, (1, 3): 1}
  528. result = _get_level_lengths(index)
  529. tm.assert_dict_equal(result, expected)
  530. def test_mi_sparse(self):
  531. df = pd.DataFrame({'A': [1, 2]},
  532. index=pd.MultiIndex.from_arrays([['a', 'a'],
  533. [0, 1]]))
  534. result = df.style._translate()
  535. body_0 = result['body'][0][0]
  536. expected_0 = {
  537. "value": "a", "display_value": "a", "is_visible": True,
  538. "type": "th", "attributes": ["rowspan=2"],
  539. "class": "row_heading level0 row0",
  540. }
  541. tm.assert_dict_equal(body_0, expected_0)
  542. body_1 = result['body'][0][1]
  543. expected_1 = {
  544. "value": 0, "display_value": 0, "is_visible": True,
  545. "type": "th", "attributes": ["rowspan=1"],
  546. "class": "row_heading level1 row0",
  547. }
  548. tm.assert_dict_equal(body_1, expected_1)
  549. body_10 = result['body'][1][0]
  550. expected_10 = {
  551. "value": 'a', "display_value": 'a', "is_visible": False,
  552. "type": "th", "attributes": ["rowspan=1"],
  553. "class": "row_heading level0 row1",
  554. }
  555. tm.assert_dict_equal(body_10, expected_10)
  556. head = result['head'][0]
  557. expected = [
  558. {'type': 'th', 'class': 'blank', 'value': '',
  559. 'is_visible': True, "display_value": ''},
  560. {'type': 'th', 'class': 'blank level0', 'value': '',
  561. 'is_visible': True, 'display_value': ''},
  562. {'attributes': ['colspan=1'], 'class': 'col_heading level0 col0',
  563. 'is_visible': True, 'type': 'th', 'value': 'A',
  564. 'display_value': 'A'}]
  565. self.assertEqual(head, expected)
  566. def test_mi_sparse_disabled(self):
  567. with pd.option_context('display.multi_sparse', False):
  568. df = pd.DataFrame({'A': [1, 2]},
  569. index=pd.MultiIndex.from_arrays([['a', 'a'],
  570. [0, 1]]))
  571. result = df.style._translate()
  572. body = result['body']
  573. for row in body:
  574. self.assertEqual(row[0]['attributes'], ['rowspan=1'])
  575. def test_mi_sparse_index_names(self):
  576. df = pd.DataFrame({'A': [1, 2]}, index=pd.MultiIndex.from_arrays(
  577. [['a', 'a'], [0, 1]],
  578. names=['idx_level_0', 'idx_level_1'])
  579. )
  580. result = df.style._translate()
  581. head = result['head'][1]
  582. expected = [{
  583. 'class': 'index_name level0', 'value': 'idx_level_0',
  584. 'type': 'th'},
  585. {'class': 'index_name level1', 'value': 'idx_level_1',
  586. 'type': 'th'},
  587. {'class': 'blank', 'value': '', 'type': 'th'}]
  588. self.assertEqual(head, expected)
  589. def test_mi_sparse_column_names(self):
  590. df = pd.DataFrame(
  591. np.arange(16).reshape(4, 4),
  592. index=pd.MultiIndex.from_arrays(
  593. [['a', 'a', 'b', 'a'], [0, 1, 1, 2]],
  594. names=['idx_level_0', 'idx_level_1']),
  595. columns=pd.MultiIndex.from_arrays(
  596. [['C1', 'C1', 'C2', 'C2'], [1, 0, 1, 0]],
  597. names=['col_0', 'col_1']
  598. )
  599. )
  600. result = df.style._translate()
  601. head = result['head'][1]
  602. expected = [
  603. {'class': 'blank', 'value': '', 'display_value': '',
  604. 'type': 'th', 'is_visible': True},
  605. {'class': 'index_name level1', 'value': 'col_1',
  606. 'display_value': 'col_1', 'is_visible': True, 'type': 'th'},
  607. {'attributes': ['colspan=1'],
  608. 'class': 'col_heading level1 col0',
  609. 'display_value': 1,
  610. 'is_visible': True,
  611. 'type': 'th',
  612. 'value': 1},
  613. {'attributes': ['colspan=1'],
  614. 'class': 'col_heading level1 col1',
  615. 'display_value': 0,
  616. 'is_visible': True,
  617. 'type': 'th',
  618. 'value': 0},
  619. {'attributes': ['colspan=1'],
  620. 'class': 'col_heading level1 col2',
  621. 'display_value': 1,
  622. 'is_visible': True,
  623. 'type': 'th',
  624. 'value': 1},
  625. {'attributes': ['colspan=1'],
  626. 'class': 'col_heading level1 col3',
  627. 'display_value': 0,
  628. 'is_visible': True,
  629. 'type': 'th',
  630. 'value': 0},
  631. ]
  632. self.assertEqual(head, expected)
  633. @tm.mplskip
  634. class TestStylerMatplotlibDep(TestCase):
  635. def test_background_gradient(self):
  636. df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B'])
  637. for axis in [0, 1, 'index', 'columns']:
  638. for cmap in [None, 'YlOrRd']:
  639. result = df.style.background_gradient(cmap=cmap)._compute().ctx
  640. self.assertTrue(all("#" in x[0] for x in result.values()))
  641. self.assertEqual(result[(0, 0)], result[(0, 1)])
  642. self.assertEqual(result[(1, 0)], result[(1, 1)])
  643. result = (df.style.background_gradient(subset=pd.IndexSlice[1, 'A'])
  644. ._compute().ctx)
  645. self.assertEqual(result[(1, 0)], ['background-color: #fff7fb'])