-
Notifications
You must be signed in to change notification settings - Fork 76
/
Copy pathdisplay.py
162 lines (144 loc) · 5.27 KB
/
display.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
import pandas as pd
import numpy as np
import re
try:
from IPython.core.display import HTML
except:
HTML = None
class TriangleDisplay:
def __repr__(self):
try:
self.values
except:
return "Empty Triangle."
if (self.values.shape[0], self.values.shape[1]) == (1, 1):
data = self._repr_format()
return data.to_string()
else:
return self._summary_frame().__repr__()
def _summary_frame(self):
return pd.Series(
[
self.valuation_date.strftime("%Y-%m"),
"O" + self.origin_grain + "D" + self.development_grain,
self.shape,
self.key_labels,
list(self.vdims),
],
index=["Valuation:", "Grain:", "Shape:", "Index:", "Columns:"],
name="Triangle Summary",
).to_frame()
def _repr_html_(self):
"""Jupyter/Ipython HTML representation"""
try:
self.values
except:
return "Triangle is empty."
if (self.values.shape[0], self.values.shape[1]) == (1, 1):
data = self._repr_format()
fmt_str = self._get_format_str(data)
default = (
data.to_html(
max_rows=pd.options.display.max_rows,
max_cols=pd.options.display.max_columns,
float_format=fmt_str.format,
)
.replace("nan", "")
.replace("NaN", "")
)
return default
else:
return self._summary_frame().to_html(
max_rows=pd.options.display.max_rows,
max_cols=pd.options.display.max_columns,
)
def _get_format_str(self, data):
if np.all(np.isnan(data)):
return ""
elif np.nanmean(abs(data)) < 10:
return "{0:,.4f}"
elif np.nanmean(abs(data)) < 1000:
return "{0:,.2f}"
else:
return "{:,.0f}"
def _repr_format(self, origin_as_datetime=False):
out = self.compute().set_backend("numpy").values[0, 0]
if origin_as_datetime and not self.is_pattern:
origin = self.origin.to_timestamp(how="s")
else:
origin = self.origin.copy()
origin.name = None
if self.origin_grain == "S" and not origin_as_datetime:
origin_formatted = [""] * len(origin)
for origin_index in range(len(origin)):
origin_formatted[origin_index] = (
origin.astype("str")[origin_index]
.replace("Q1", "H1")
.replace("Q3", "H2")
)
origin = origin_formatted
development = self.development.copy()
development.name = None
return pd.DataFrame(out, index=origin, columns=development)
def heatmap(self, cmap="coolwarm", low=0, high=0, axis=0, subset=None):
"""Color the background in a gradient according to the data in each
column (optionally row). Requires matplotlib
Parameters
----------
cmap : str or colormap
matplotlib colormap
low, high : float
compress the range by these values.
axis : int or str
The axis along which to apply heatmap
subset : IndexSlice
a valid slice for data to limit the style application to
Returns
-------
Ipython.display.HTML
"""
if (self.values.shape[0], self.values.shape[1]) == (1, 1):
data = self._repr_format()
fmt_str = self._get_format_str(data)
axis = self._get_axis(axis)
raw_rank = data.rank(axis=axis)
shape_size = data.shape[axis]
rank_size = data.rank(axis=axis).max(axis=axis)
gmap = (raw_rank - 1).div(rank_size - 1, axis=not axis) * (
shape_size - 1
) + 1
gmap = gmap.replace(np.nan, (shape_size + 1) / 2)
if pd.__version__ >= "1.3":
default_output = (
data.style.format(fmt_str)
.background_gradient(
cmap=cmap,
low=low,
high=high,
axis=None,
subset=subset,
gmap=gmap,
)
.to_html()
)
else:
default_output = (
data.style.format(fmt_str)
.background_gradient(
cmap=cmap,
low=low,
high=high,
axis=axis,
)
.render()
)
output_xnan = re.sub("<td.*nan.*td>", "<td></td>", default_output)
else:
raise ValueError("heatmap() only works with a single triangle")
if HTML:
return HTML(output_xnan)
elif HTML is None:
raise ImportError("heatmap requires IPython")