MatplotLibAPI 3.1.0__py3-none-any.whl → 3.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- MatplotLibAPI/Bubble.py +3 -4
- MatplotLibAPI/Composite.py +7 -5
- MatplotLibAPI/Network.py +57 -22
- MatplotLibAPI/Pivot.py +6 -5
- MatplotLibAPI/Table.py +3 -2
- MatplotLibAPI/Timeserie.py +5 -4
- MatplotLibAPI/Treemap.py +36 -12
- MatplotLibAPI/__init__.py +307 -11
- {MatplotLibAPI-3.1.0.dist-info → MatplotLibAPI-3.1.2.dist-info}/METADATA +10 -2
- MatplotLibAPI-3.1.2.dist-info/RECORD +13 -0
- MatplotLibAPI/Style.py +0 -171
- MatplotLibAPI/pdAccessor.py +0 -147
- MatplotLibAPI-3.1.0.dist-info/RECORD +0 -15
- {MatplotLibAPI-3.1.0.dist-info → MatplotLibAPI-3.1.2.dist-info}/LICENSE +0 -0
- {MatplotLibAPI-3.1.0.dist-info → MatplotLibAPI-3.1.2.dist-info}/WHEEL +0 -0
- {MatplotLibAPI-3.1.0.dist-info → MatplotLibAPI-3.1.2.dist-info}/top_level.txt +0 -0
MatplotLibAPI/Bubble.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# Hint for Visual Code Python Interactive window
|
|
2
2
|
# %%
|
|
3
|
-
|
|
3
|
+
from typing import Optional
|
|
4
4
|
import pandas as pd
|
|
5
5
|
import matplotlib.pyplot as plt
|
|
6
6
|
from matplotlib.axes import Axes
|
|
7
7
|
import seaborn as sns
|
|
8
|
-
from typing import Optional
|
|
9
|
-
from .Style import DynamicFuncFormatter, StyleTemplate, generate_ticks, _validate_panda, string_formatter, bmk_formatter, percent_formatter, format_func
|
|
10
8
|
|
|
9
|
+
from . import DynamicFuncFormatter, StyleTemplate, generate_ticks, string_formatter, bmk_formatter, percent_formatter, format_func,validate_dataframe
|
|
11
10
|
|
|
12
11
|
BUBBLE_STYLE_TEMPLATE = StyleTemplate(
|
|
13
12
|
format_funcs={"label": string_formatter,
|
|
@@ -33,7 +32,7 @@ def plot_bubble(
|
|
|
33
32
|
ascending: bool = False,
|
|
34
33
|
ax: Optional[Axes] = None):
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
validate_dataframe(pd_df, cols=[label, x, y, z], sort_by=sort_by)
|
|
37
36
|
style.format_funcs = format_func(
|
|
38
37
|
style.format_funcs, label=label, x=x, y=y, z=z)
|
|
39
38
|
if not sort_by:
|
MatplotLibAPI/Composite.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Hint for Visual Code Python Interactive window
|
|
2
2
|
# %%
|
|
3
|
+
from typing import Optional, Tuple
|
|
4
|
+
import pandas as pd
|
|
3
5
|
import matplotlib.pyplot as plt
|
|
4
6
|
from matplotlib.figure import Figure
|
|
5
|
-
|
|
7
|
+
|
|
8
|
+
from .Network import plot_network, plot_network_components, DEFAULT
|
|
6
9
|
from .Bubble import plot_bubble, BUBBLE_STYLE_TEMPLATE
|
|
7
10
|
from .Table import plot_table
|
|
8
|
-
from
|
|
9
|
-
from .Style import StyleTemplate, _validate_panda, format_func
|
|
11
|
+
from . import StyleTemplate, format_func, validate_dataframe
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
def plot_composite_bubble(
|
|
@@ -19,13 +21,13 @@ def plot_composite_bubble(
|
|
|
19
21
|
style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
|
|
20
22
|
max_values: int = 50,
|
|
21
23
|
center_to_mean: bool = False,
|
|
22
|
-
filter_by:Optional[str] = None,
|
|
24
|
+
filter_by: Optional[str] = None,
|
|
23
25
|
sort_by: Optional[str] = None,
|
|
24
26
|
ascending: bool = False,
|
|
25
27
|
table_rows: int = 10,
|
|
26
28
|
figsize: Tuple[float, float] = (19.2, 10.8)) -> Figure:
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
validate_dataframe(pd_df, cols=[label, x, y, z], sort_by=sort_by)
|
|
29
31
|
|
|
30
32
|
if not sort_by:
|
|
31
33
|
sort_by = z
|
MatplotLibAPI/Network.py
CHANGED
|
@@ -13,7 +13,7 @@ from networkx import Graph
|
|
|
13
13
|
from networkx.classes.graph import Graph
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
from .
|
|
16
|
+
from . import StyleTemplate, string_formatter, format_func,validate_dataframe
|
|
17
17
|
|
|
18
18
|
NETWORK_STYLE_TEMPLATE = StyleTemplate(
|
|
19
19
|
)
|
|
@@ -165,23 +165,22 @@ class Graph(nx.Graph):
|
|
|
165
165
|
|
|
166
166
|
return node_size, edges_width, fonts_size
|
|
167
167
|
|
|
168
|
-
def
|
|
168
|
+
def subgraph(self, node_list=None, max_edges: int = DEFAULT["MAX_EDGES"]) -> Graph:
|
|
169
169
|
if node_list is None:
|
|
170
170
|
node_list = self.nodes.sort("weight")[:DEFAULT["MAX_NODES"]]
|
|
171
171
|
connected_subgraph_nodes = list(self.find_connected_subgraph())
|
|
172
172
|
node_list = [
|
|
173
173
|
node for node in node_list if node in connected_subgraph_nodes]
|
|
174
174
|
|
|
175
|
-
subgraph = nx.subgraph(
|
|
176
|
-
self, nbunch=node_list)
|
|
175
|
+
subgraph = nx.subgraph(self, nbunch=node_list)
|
|
177
176
|
edges = subgraph.top_k_edges(attribute="weight", k=5).keys()
|
|
178
177
|
subgraph = subgraph.edge_subgraph(list(edges)[:max_edges])
|
|
179
|
-
return subgraph
|
|
178
|
+
return Graph(subgraph)
|
|
180
179
|
|
|
181
|
-
def
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
def plot_network(self,
|
|
181
|
+
title: str = "Test",
|
|
182
|
+
style: StyleTemplate = NETWORK_STYLE_TEMPLATE,
|
|
183
|
+
ax: Optional[Axes] = None) -> Axes:
|
|
185
184
|
"""
|
|
186
185
|
Plots the degree distribution of the graph, including a degree rank plot and a degree histogram.
|
|
187
186
|
"""
|
|
@@ -227,7 +226,7 @@ class Graph(nx.Graph):
|
|
|
227
226
|
|
|
228
227
|
return ax
|
|
229
228
|
|
|
230
|
-
def
|
|
229
|
+
def plot_network_components(self, node_list: Optional[List] = None,
|
|
231
230
|
scale: int = DEFAULT["GRAPH_SCALE"],
|
|
232
231
|
node_scale: int = DEFAULT["MAX_NODE_SIZE"],
|
|
233
232
|
edge_scale: float = DEFAULT["MAX_EDGE_WIDTH"],
|
|
@@ -235,13 +234,16 @@ class Graph(nx.Graph):
|
|
|
235
234
|
max_edges: int = DEFAULT["MAX_EDGES"],
|
|
236
235
|
plt_title: Optional[str] = "Top keywords"):
|
|
237
236
|
# node_list=self.nodes_circuits(node_list)
|
|
238
|
-
g = self.
|
|
237
|
+
g = self.subgraph(max_edges=max_edges, node_list=node_list)
|
|
239
238
|
connected_components = nx.connected_components(g)
|
|
239
|
+
axes=[]
|
|
240
240
|
for connected_component in connected_components:
|
|
241
241
|
if len(connected_component) > 5:
|
|
242
|
-
connected_component_graph = self.
|
|
243
|
-
|
|
244
|
-
connected_component_graph.
|
|
242
|
+
connected_component_graph = self.subgraph(max_edges=max_edges,
|
|
243
|
+
node_list=connected_component)
|
|
244
|
+
ax=connected_component_graph.plot_network()
|
|
245
|
+
axes.append(ax)
|
|
246
|
+
return axes
|
|
245
247
|
|
|
246
248
|
def find_connected_subgraph(self):
|
|
247
249
|
logging.info(f'find_connected_subgraph')
|
|
@@ -295,7 +297,7 @@ class Graph(nx.Graph):
|
|
|
295
297
|
def from_pandas_edgelist(df: pd.DataFrame,
|
|
296
298
|
source: str = "source",
|
|
297
299
|
target: str = "target",
|
|
298
|
-
weight: str = "weight"):
|
|
300
|
+
weight: str = "weight") -> Graph:
|
|
299
301
|
"""
|
|
300
302
|
Initialize netX instance with a simple dataframe
|
|
301
303
|
|
|
@@ -305,7 +307,6 @@ class Graph(nx.Graph):
|
|
|
305
307
|
:param weight: Name of edges weight column in df_source.
|
|
306
308
|
|
|
307
309
|
"""
|
|
308
|
-
G = Graph()
|
|
309
310
|
G = nx.from_pandas_edgelist(
|
|
310
311
|
df, source=source, target=target, edge_attr=weight, create_using=G)
|
|
311
312
|
G = G.find_connected_subgraph()
|
|
@@ -323,7 +324,7 @@ class Graph(nx.Graph):
|
|
|
323
324
|
nx.set_node_attributes(G, node_aggregates, name=weight)
|
|
324
325
|
|
|
325
326
|
G = G.edge_subgraph(edges=G.top_k_edges(attribute=weight))
|
|
326
|
-
return G
|
|
327
|
+
return Graph(G)
|
|
327
328
|
|
|
328
329
|
|
|
329
330
|
def plot_network(pd_df: pd.DataFrame,
|
|
@@ -333,15 +334,49 @@ def plot_network(pd_df: pd.DataFrame,
|
|
|
333
334
|
title: str = "Test",
|
|
334
335
|
style: StyleTemplate = NETWORK_STYLE_TEMPLATE,
|
|
335
336
|
sort_by: Optional[str] = None,
|
|
336
|
-
ascending: bool = False,
|
|
337
|
+
ascending: bool = False,
|
|
338
|
+
node_list: Optional[List] = None,
|
|
337
339
|
ax: Optional[Axes] = None) -> Axes:
|
|
340
|
+
if node_list:
|
|
341
|
+
df = pd_df[(pd_df["source"].isin(node_list)) | (pd_df["target"].isin(node_list))]
|
|
342
|
+
else:
|
|
343
|
+
df = pd_df
|
|
344
|
+
validate_dataframe(df, cols=[source, target, weight], sort_by=sort_by)
|
|
338
345
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
graph = Graph.from_pandas_edgelist(pd_df,
|
|
346
|
+
graph = Graph.from_pandas_edgelist(df,
|
|
342
347
|
source=source,
|
|
343
348
|
target=target,
|
|
344
349
|
weight=weight)
|
|
345
|
-
return graph.
|
|
350
|
+
return graph.plot_network(title=title,
|
|
346
351
|
style=style,
|
|
347
352
|
ax=ax)
|
|
353
|
+
|
|
354
|
+
def plot_network_components(pd_df: pd.DataFrame,
|
|
355
|
+
source: str = "source",
|
|
356
|
+
target: str = "target",
|
|
357
|
+
weight: str = "weight",
|
|
358
|
+
title: str = "Test",
|
|
359
|
+
style: StyleTemplate = NETWORK_STYLE_TEMPLATE,
|
|
360
|
+
sort_by: Optional[str] = None,
|
|
361
|
+
node_list: Optional[List] = None,
|
|
362
|
+
ascending: bool = False,
|
|
363
|
+
ax: Optional[List[Axes]] = None) -> List[Axes]:
|
|
364
|
+
if node_list:
|
|
365
|
+
df = pd_df[(pd_df["source"].isin(node_list)) | (pd_df["target"].isin(node_list))]
|
|
366
|
+
else:
|
|
367
|
+
df = pd_df
|
|
368
|
+
validate_dataframe(df, cols=[source, target, weight], sort_by=sort_by)
|
|
369
|
+
|
|
370
|
+
graph = Graph.from_pandas_edgelist(df,
|
|
371
|
+
source=source,
|
|
372
|
+
target=target,
|
|
373
|
+
weight=weight)
|
|
374
|
+
connected_components = nx.connected_components(graph)
|
|
375
|
+
axes=[]
|
|
376
|
+
for connected_component in connected_components:
|
|
377
|
+
if len(connected_component) > 5:
|
|
378
|
+
connected_component_graph = graph.subgraph(node_list=connected_component)
|
|
379
|
+
ax=connected_component_graph.plot_network()
|
|
380
|
+
axes.append(ax)
|
|
381
|
+
return axes
|
|
382
|
+
|
MatplotLibAPI/Pivot.py
CHANGED
|
@@ -10,15 +10,15 @@ from matplotlib.axes import Axes
|
|
|
10
10
|
from matplotlib.dates import DateFormatter, MonthLocator
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
from
|
|
13
|
+
from . import DynamicFuncFormatter, StyleTemplate, generate_ticks, string_formatter, percent_formatter, format_func
|
|
14
|
+
from .. import validate_dataframe
|
|
15
15
|
|
|
16
16
|
PIVOTBARS_STYLE_TEMPLATE = StyleTemplate(
|
|
17
17
|
background_color='black',
|
|
18
18
|
fig_border='darkgrey',
|
|
19
19
|
font_color='white',
|
|
20
20
|
palette='magma',
|
|
21
|
-
format_funcs={"y": percent_formatter,
|
|
21
|
+
format_funcs={"y": percent_formatter,
|
|
22
22
|
"label": string_formatter}
|
|
23
23
|
)
|
|
24
24
|
PIVOTLINES_STYLE_TEMPLATE = StyleTemplate(
|
|
@@ -28,6 +28,7 @@ PIVOTLINES_STYLE_TEMPLATE = StyleTemplate(
|
|
|
28
28
|
format_funcs={"y": percent_formatter, "label": string_formatter}
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
+
|
|
31
32
|
def plot_pivotbar(pd_df: pd.DataFrame,
|
|
32
33
|
label: str,
|
|
33
34
|
x: str,
|
|
@@ -39,8 +40,8 @@ def plot_pivotbar(pd_df: pd.DataFrame,
|
|
|
39
40
|
ascending: bool = False,
|
|
40
41
|
ax: Optional[Axes] = None):
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
style.format_funcs=format_func(style.format_funcs,label=label,x=x,y=y)
|
|
43
|
+
validate_dataframe(pd_df, cols=[label, x, y], sort_by=sort_by)
|
|
44
|
+
style.format_funcs = format_func(style.format_funcs, label=label, x=x, y=y)
|
|
44
45
|
pivot_df = pd.pivot_table(pd_df, values=y, index=[
|
|
45
46
|
x], columns=[label], aggfunc=agg)
|
|
46
47
|
# Reset index to make x a column again
|
MatplotLibAPI/Table.py
CHANGED
|
@@ -2,7 +2,8 @@ from typing import List, Optional
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
import matplotlib.pyplot as plt
|
|
4
4
|
from matplotlib.axes import Axes
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
from . import StyleTemplate, string_formatter,validate_dataframe
|
|
6
7
|
|
|
7
8
|
TABLE_STYLE_TEMPLATE = StyleTemplate(
|
|
8
9
|
background_color='black',
|
|
@@ -21,7 +22,7 @@ def plot_table(pd_df: pd.DataFrame,
|
|
|
21
22
|
ascending: bool = False,
|
|
22
23
|
ax: Optional[Axes] = None
|
|
23
24
|
) -> Axes:
|
|
24
|
-
|
|
25
|
+
validate_dataframe(pd_df, cols=cols, sort_by=sort_by)
|
|
25
26
|
|
|
26
27
|
if not sort_by:
|
|
27
28
|
sort_by = cols[0]
|
MatplotLibAPI/Timeserie.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# Hint for Visual Code Python Interactive window
|
|
2
2
|
# %%
|
|
3
|
+
from typing import Optional
|
|
3
4
|
import pandas as pd
|
|
4
|
-
|
|
5
5
|
import matplotlib.pyplot as plt
|
|
6
6
|
from matplotlib.axes import Axes
|
|
7
7
|
import seaborn as sns
|
|
8
|
-
|
|
9
|
-
from
|
|
8
|
+
|
|
9
|
+
from . import DynamicFuncFormatter, StyleTemplate, string_formatter, bmk_formatter, format_func,validate_dataframe
|
|
10
|
+
|
|
10
11
|
|
|
11
12
|
TIMESERIE_STYLE_TEMPLATE = StyleTemplate(
|
|
12
13
|
palette='rocket',
|
|
@@ -26,7 +27,7 @@ def plot_timeserie(pd_df: pd.DataFrame,
|
|
|
26
27
|
ascending: bool = False,
|
|
27
28
|
ax: Optional[Axes] = None) -> Axes:
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
validate_dataframe(pd_df, cols=[label, x, y], sort_by=sort_by)
|
|
30
31
|
style.format_funcs = format_func(style.format_funcs, label=label, x=x, y=y)
|
|
31
32
|
|
|
32
33
|
df = pd_df[[label, x, y]].sort_values(by=[label, x])
|
MatplotLibAPI/Treemap.py
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
# Hint for Visual Code Python Interactive window
|
|
2
|
+
# %%
|
|
3
|
+
from typing import Optional
|
|
2
4
|
import pandas as pd
|
|
5
|
+
from pandas import CategoricalDtype,BooleanDtype
|
|
3
6
|
import plotly.graph_objects as go
|
|
4
|
-
|
|
7
|
+
|
|
8
|
+
from . import StyleTemplate, string_formatter, percent_formatter,validate_dataframe
|
|
9
|
+
|
|
10
|
+
|
|
5
11
|
|
|
6
12
|
TREEMAP_STYLE_TEMPLATE = StyleTemplate(
|
|
7
13
|
background_color='black',
|
|
@@ -26,7 +32,7 @@ def plot_treemap(pd_df: pd.DataFrame,
|
|
|
26
32
|
cols = [path, values]
|
|
27
33
|
if color:
|
|
28
34
|
cols.append(color)
|
|
29
|
-
|
|
35
|
+
validate_dataframe(pd_df, cols=cols, sort_by=sort_by)
|
|
30
36
|
if not sort_by:
|
|
31
37
|
sort_by = values
|
|
32
38
|
df = pd_df.sort_values(by=sort_by, ascending=ascending)[
|
|
@@ -35,21 +41,39 @@ def plot_treemap(pd_df: pd.DataFrame,
|
|
|
35
41
|
"parents": [""] * len(df),
|
|
36
42
|
"values": df[values],
|
|
37
43
|
"textinfo": "label",
|
|
38
|
-
"name": title
|
|
44
|
+
"name": title,
|
|
45
|
+
"textfont":
|
|
46
|
+
{"family": style.font_name,
|
|
47
|
+
"size": style.font_size,
|
|
48
|
+
"color": style.font_color}
|
|
49
|
+
}
|
|
39
50
|
|
|
40
|
-
if color:
|
|
41
|
-
|
|
51
|
+
if color and color in pd_df.columns:
|
|
52
|
+
color_data = pd_df[color]
|
|
53
|
+
if isinstance(color_data, CategoricalDtype) or pd.api.types.is_object_dtype(color_data):
|
|
54
|
+
color_data = color_data.astype('category').cat.codes
|
|
55
|
+
elif isinstance(color_data, BooleanDtype):
|
|
56
|
+
color_data = color_data.astype(int)
|
|
57
|
+
data['marker'] = dict(colorscale="Viridis",
|
|
58
|
+
colors=color_data.to_list())
|
|
59
|
+
|
|
60
|
+
g = go.Treemap(data)
|
|
42
61
|
|
|
43
62
|
if not fig:
|
|
44
|
-
fig = go.Figure(
|
|
63
|
+
fig = go.Figure(g)
|
|
45
64
|
else:
|
|
46
|
-
fig.add_trace(
|
|
47
|
-
|
|
65
|
+
fig.add_trace(g)
|
|
66
|
+
|
|
48
67
|
fig.update_layout(
|
|
49
|
-
|
|
68
|
+
title=title,
|
|
50
69
|
plot_bgcolor=style.background_color,
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
paper_bgcolor=style.background_color,
|
|
71
|
+
font=dict(
|
|
72
|
+
family=style.font_name,
|
|
73
|
+
size=style.font_size,
|
|
74
|
+
color=style.font_color
|
|
75
|
+
),
|
|
76
|
+
showlegend=style.legend if style else True)
|
|
53
77
|
|
|
54
78
|
# Apply color scale
|
|
55
79
|
fig.update_traces(
|
MatplotLibAPI/__init__.py
CHANGED
|
@@ -1,14 +1,310 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
|
-
from .
|
|
4
|
-
from .
|
|
5
|
-
from .
|
|
6
|
-
from .Network import plot_network
|
|
7
|
-
from .Pivot import plot_pivotbar
|
|
8
|
-
from .Treemap import plot_treemap
|
|
2
|
+
from .Treemap import plot_treemap, TREEMAP_STYLE_TEMPLATE
|
|
3
|
+
from .Network import Graph
|
|
4
|
+
from .Table import plot_table, TABLE_STYLE_TEMPLATE
|
|
5
|
+
from .Timeserie import plot_timeserie, TIMESERIE_STYLE_TEMPLATE
|
|
9
6
|
from .Composite import plot_composite_bubble
|
|
10
|
-
from .
|
|
11
|
-
from
|
|
7
|
+
from .Bubble import plot_bubble, BUBBLE_STYLE_TEMPLATE
|
|
8
|
+
from typing import List, Optional, Dict, Callable, Union
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
import pandas as pd
|
|
11
|
+
from pandas.api.extensions import register_dataframe_accessor
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from matplotlib.axes import Axes
|
|
15
|
+
from matplotlib.figure import Figure
|
|
16
|
+
from matplotlib.dates import num2date
|
|
17
|
+
from matplotlib.ticker import FuncFormatter
|
|
18
|
+
import plotly.graph_objects as go
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# region Utils
|
|
22
|
+
|
|
23
|
+
def validate_dataframe(pd_df: pd.DataFrame,
|
|
24
|
+
cols: List[str],
|
|
25
|
+
sort_by: Optional[str] = None):
|
|
26
|
+
_columns = cols.copy()
|
|
27
|
+
if sort_by and sort_by not in _columns:
|
|
28
|
+
_columns.append(sort_by)
|
|
29
|
+
for col in _columns:
|
|
30
|
+
if col not in pd_df.columns:
|
|
31
|
+
raise AttributeError(f"{col} is not a DataFrame's column")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def format_func(
|
|
35
|
+
format_funcs: Optional[Dict[str, Optional[Callable[[Union[int, float, str]], str]]]],
|
|
36
|
+
label: Optional[str] = None,
|
|
37
|
+
x: Optional[str] = None,
|
|
38
|
+
y: Optional[str] = None,
|
|
39
|
+
z: Optional[str] = None):
|
|
40
|
+
|
|
41
|
+
if label and "label" in format_funcs:
|
|
42
|
+
format_funcs[label] = format_funcs["label"]
|
|
43
|
+
if x and "x" in format_funcs:
|
|
44
|
+
format_funcs[x] = format_funcs["x"]
|
|
45
|
+
if y and "y" in format_funcs:
|
|
46
|
+
format_funcs[y] = format_funcs["y"]
|
|
47
|
+
if z and "z" in format_funcs:
|
|
48
|
+
format_funcs[z] = format_funcs["z"]
|
|
49
|
+
return format_funcs
|
|
50
|
+
|
|
51
|
+
# endregion
|
|
52
|
+
|
|
53
|
+
# region Style
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
MAX_RESULTS = 50
|
|
57
|
+
X_COL = "index"
|
|
58
|
+
Y_COL = "overlap"
|
|
59
|
+
Z_COL = "users"
|
|
60
|
+
FIG_SIZE = (19.2, 10.8)
|
|
61
|
+
BACKGROUND_COLOR = 'black'
|
|
62
|
+
TEXT_COLOR = 'white'
|
|
63
|
+
PALETTE = "Greys_r"
|
|
64
|
+
FONT_SIZE = 14
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class StyleTemplate:
|
|
69
|
+
background_color: str = BACKGROUND_COLOR
|
|
70
|
+
fig_border: str = BACKGROUND_COLOR
|
|
71
|
+
font_name: str = 'Arial'
|
|
72
|
+
font_size: int = FONT_SIZE
|
|
73
|
+
font_color: str = TEXT_COLOR
|
|
74
|
+
palette: str = PALETTE
|
|
75
|
+
legend: bool = True
|
|
76
|
+
xscale: Optional[str] = None
|
|
77
|
+
x_ticks: int = 10
|
|
78
|
+
yscale: Optional[str] = None
|
|
79
|
+
y_ticks: int = 5
|
|
80
|
+
format_funcs: Optional[Dict[str, Optional[Callable[[
|
|
81
|
+
Union[int, float, str]], str]]]] = None
|
|
82
|
+
col_widths: Optional[List[float]] = None
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def font_mapping(self):
|
|
86
|
+
return {0: self.font_size-3,
|
|
87
|
+
1: self.font_size-1,
|
|
88
|
+
2: self.font_size,
|
|
89
|
+
3: self.font_size+1,
|
|
90
|
+
4: self.font_size+3}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class DynamicFuncFormatter(FuncFormatter):
|
|
94
|
+
def __init__(self, func_name):
|
|
95
|
+
super().__init__(func_name)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def percent_formatter(val, pos: Optional[int] = None):
|
|
99
|
+
if val*100 <= 0.1: # For 0.1%
|
|
100
|
+
return f"{val*100:.2f}%"
|
|
101
|
+
elif val*100 <= 1: # For 1%
|
|
102
|
+
return f"{val*100:.1f}%"
|
|
103
|
+
else:
|
|
104
|
+
return f"{val*100:.0f}%"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def bmk_formatter(val, pos: Optional[int] = None):
|
|
108
|
+
if val >= 1_000_000_000: # Billions
|
|
109
|
+
return f"{val / 1_000_000_000:.2f}B"
|
|
110
|
+
elif val >= 1_000_000: # Millions
|
|
111
|
+
return f"{val / 1_000_000:.1f}M"
|
|
112
|
+
elif val >= 1_000: # Thousands
|
|
113
|
+
return f"{val / 1_000:.1f}K"
|
|
114
|
+
else:
|
|
115
|
+
return f"{int(val)}"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def integer_formatter(value, pos: Optional[int] = None):
|
|
119
|
+
return f"{int(value)}"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def string_formatter(value, pos: Optional[int] = None):
|
|
123
|
+
return str(value).replace("-", " ").replace("_", " ").title()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def yy_mm__formatter(x, pos: Optional[int] = None):
|
|
127
|
+
return num2date(x).strftime('%Y-%m')
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def yy_mm_dd__formatter(x, pos: Optional[int] = None):
|
|
131
|
+
return num2date(x).strftime('%Y-%m-%D')
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def percent_formatter(x, pos: Optional[int] = None):
|
|
135
|
+
return f"{x * 100:.0f}%"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def generate_ticks(min_val, max_val, num_ticks="10"):
|
|
139
|
+
# Identify the type of the input
|
|
140
|
+
try:
|
|
141
|
+
min_val = float(min_val)
|
|
142
|
+
max_val = float(max_val)
|
|
143
|
+
is_date = False
|
|
144
|
+
except ValueError:
|
|
145
|
+
is_date = True
|
|
146
|
+
|
|
147
|
+
# Convert string inputs to appropriate numerical or date types
|
|
148
|
+
num_ticks = int(num_ticks)
|
|
149
|
+
|
|
150
|
+
if is_date:
|
|
151
|
+
min_val = pd.Timestamp(min_val).to_datetime64()
|
|
152
|
+
max_val = pd.Timestamp(max_val).to_datetime64()
|
|
153
|
+
data_range = (max_val - min_val).astype('timedelta64[D]').astype(int)
|
|
154
|
+
else:
|
|
155
|
+
data_range = max_val - min_val
|
|
156
|
+
|
|
157
|
+
# Calculate a nice step size
|
|
158
|
+
step_size = data_range / (num_ticks - 1)
|
|
159
|
+
|
|
160
|
+
# If date, convert back to datetime
|
|
161
|
+
if is_date:
|
|
162
|
+
ticks = pd.date_range(
|
|
163
|
+
start=min_val, periods=num_ticks, freq=f"{step_size}D")
|
|
164
|
+
else:
|
|
165
|
+
# Round the step size to a "nice" number
|
|
166
|
+
exponent = np.floor(np.log10(step_size))
|
|
167
|
+
fraction = step_size / 10**exponent
|
|
168
|
+
nice_fraction = round(fraction)
|
|
169
|
+
|
|
170
|
+
# Create nice step size
|
|
171
|
+
nice_step = nice_fraction * 10**exponent
|
|
172
|
+
|
|
173
|
+
# Generate the tick marks based on the nice step size
|
|
174
|
+
ticks = np.arange(min_val, max_val + nice_step, nice_step)
|
|
175
|
+
|
|
176
|
+
return ticks
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# endregion
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@register_dataframe_accessor("mpl")
|
|
183
|
+
class DataFrameAccessor:
|
|
184
|
+
|
|
185
|
+
def __init__(self, pd_df: pd.DataFrame):
|
|
186
|
+
self._obj = pd_df
|
|
187
|
+
|
|
188
|
+
def plot_bubble(self,
|
|
189
|
+
label: str,
|
|
190
|
+
x: str,
|
|
191
|
+
y: str,
|
|
192
|
+
z: str,
|
|
193
|
+
title: Optional[str] = None,
|
|
194
|
+
style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
|
|
195
|
+
max_values: int = 50,
|
|
196
|
+
center_to_mean: bool = False,
|
|
197
|
+
sort_by: Optional[str] = None,
|
|
198
|
+
ascending: bool = False) -> Axes:
|
|
199
|
+
|
|
200
|
+
return plot_bubble(pd_df=self._obj,
|
|
201
|
+
label=label,
|
|
202
|
+
x=x,
|
|
203
|
+
y=y,
|
|
204
|
+
z=z,
|
|
205
|
+
title=title,
|
|
206
|
+
style=style,
|
|
207
|
+
max_values=max_values,
|
|
208
|
+
center_to_mean=center_to_mean,
|
|
209
|
+
sort_by=sort_by,
|
|
210
|
+
ascending=ascending)
|
|
211
|
+
|
|
212
|
+
def plot_composite_bubble(self,
|
|
213
|
+
label: str,
|
|
214
|
+
x: str,
|
|
215
|
+
y: str,
|
|
216
|
+
z: str,
|
|
217
|
+
title: Optional[str] = None,
|
|
218
|
+
style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
|
|
219
|
+
max_values: int = 100,
|
|
220
|
+
center_to_mean: bool = False,
|
|
221
|
+
sort_by: Optional[str] = None,
|
|
222
|
+
ascending: bool = False) -> Figure:
|
|
223
|
+
|
|
224
|
+
return plot_composite_bubble(pd_df=self._obj,
|
|
225
|
+
label=label,
|
|
226
|
+
x=x,
|
|
227
|
+
y=y,
|
|
228
|
+
z=z,
|
|
229
|
+
title=title,
|
|
230
|
+
style=style,
|
|
231
|
+
max_values=max_values,
|
|
232
|
+
center_to_mean=center_to_mean,
|
|
233
|
+
sort_by=sort_by,
|
|
234
|
+
ascending=ascending)
|
|
235
|
+
|
|
236
|
+
def plot_table(self,
|
|
237
|
+
cols: List[str],
|
|
238
|
+
title: Optional[str] = None,
|
|
239
|
+
style: StyleTemplate = TABLE_STYLE_TEMPLATE,
|
|
240
|
+
max_values: int = 20,
|
|
241
|
+
sort_by: Optional[str] = None,
|
|
242
|
+
ascending: bool = False) -> Axes:
|
|
243
|
+
|
|
244
|
+
return plot_table(pd_df=self._obj,
|
|
245
|
+
cols=cols,
|
|
246
|
+
title=title,
|
|
247
|
+
style=style,
|
|
248
|
+
max_values=max_values,
|
|
249
|
+
sort_by=sort_by,
|
|
250
|
+
ascending=ascending)
|
|
251
|
+
|
|
252
|
+
def plot_timeserie(self,
|
|
253
|
+
label: str,
|
|
254
|
+
x: str,
|
|
255
|
+
y: str,
|
|
256
|
+
title: Optional[str] = None,
|
|
257
|
+
style: StyleTemplate = TIMESERIE_STYLE_TEMPLATE,
|
|
258
|
+
max_values: int = 100,
|
|
259
|
+
sort_by: Optional[str] = None,
|
|
260
|
+
ascending: bool = False) -> Axes:
|
|
261
|
+
|
|
262
|
+
return plot_timeserie(pd_df=self._obj,
|
|
263
|
+
label=label,
|
|
264
|
+
x=x,
|
|
265
|
+
y=y,
|
|
266
|
+
title=title,
|
|
267
|
+
style=style,
|
|
268
|
+
max_values=max_values,
|
|
269
|
+
sort_by=sort_by,
|
|
270
|
+
ascending=ascending)
|
|
271
|
+
|
|
272
|
+
def plot_network(self,
|
|
273
|
+
source: str = "source",
|
|
274
|
+
target: str = "target",
|
|
275
|
+
weight: str = "weight",
|
|
276
|
+
title: Optional[str] = None,
|
|
277
|
+
style: StyleTemplate = TIMESERIE_STYLE_TEMPLATE,
|
|
278
|
+
max_values: int = 20,
|
|
279
|
+
sort_by: Optional[str] = None,
|
|
280
|
+
ascending: bool = False) -> Axes:
|
|
281
|
+
|
|
282
|
+
graph = Graph.from_pandas_edgelist(df=self._obj,
|
|
283
|
+
source=source,
|
|
284
|
+
target=target,
|
|
285
|
+
weight=weight)
|
|
286
|
+
|
|
287
|
+
return graph.plotX(title, style)
|
|
288
|
+
|
|
289
|
+
def plot_treemap(self,
|
|
290
|
+
path: str,
|
|
291
|
+
values: str,
|
|
292
|
+
style: StyleTemplate = TREEMAP_STYLE_TEMPLATE,
|
|
293
|
+
title: Optional[str] = None,
|
|
294
|
+
color: Optional[str] = None,
|
|
295
|
+
max_values: int = 100,
|
|
296
|
+
sort_by: Optional[str] = None,
|
|
297
|
+
ascending: bool = False) -> go.Figure:
|
|
298
|
+
return plot_treemap(pd_df=self._obj,
|
|
299
|
+
path=path,
|
|
300
|
+
values=values,
|
|
301
|
+
title=title,
|
|
302
|
+
style=style,
|
|
303
|
+
color=color,
|
|
304
|
+
max_values=max_values,
|
|
305
|
+
sort_by=sort_by,
|
|
306
|
+
ascending=ascending)
|
|
307
|
+
|
|
12
308
|
|
|
13
|
-
__all__ = ["plot_bubble", "plot_timeserie", "plot_table", "plot_network",
|
|
14
|
-
"plot_pivotbar", "plot_treemap","plot_composite_bubble", "StyleTemplate", "
|
|
309
|
+
__all__ = ["validate_dataframe", "plot_bubble", "plot_timeserie", "plot_table", "plot_network", "plot_network_components",
|
|
310
|
+
"plot_pivotbar", "plot_treemap", "plot_composite_bubble", "StyleTemplate", "DataFrameAccessor"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: MatplotLibAPI
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.2
|
|
4
4
|
Requires-Python: >=3.7
|
|
5
5
|
Description-Content-Type: text/markdown
|
|
6
6
|
License-File: LICENSE
|
|
@@ -10,6 +10,14 @@ Requires-Dist: networkx
|
|
|
10
10
|
Requires-Dist: plotly
|
|
11
11
|
Requires-Dist: seaborn
|
|
12
12
|
Requires-Dist: scikit-learn
|
|
13
|
+
Requires-Dist: kaleido
|
|
14
|
+
Requires-Dist: nbformat
|
|
13
15
|
|
|
14
16
|
# MatplotLibAPI
|
|
15
|
-
Simple
|
|
17
|
+
Simple pandas Dataframe to generate plots:
|
|
18
|
+
Bubble (Scatter plot)
|
|
19
|
+
Network (Graph)
|
|
20
|
+
pivot
|
|
21
|
+
Table
|
|
22
|
+
Timeserie
|
|
23
|
+
Treemap
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
MatplotLibAPI/Bubble.py,sha256=zfm5Y2YU824TzvFb_57HeeTgOXgx0oozBRcxqr6g_lo,3829
|
|
2
|
+
MatplotLibAPI/Composite.py,sha256=V5VgYt6cSJkz74XtMoQHHr9VrJXojCFsmxjkqwA0oYc,2834
|
|
3
|
+
MatplotLibAPI/Network.py,sha256=zdu6kVIja0peYLL4mdpw76BijLn6hAmtoKbK_YukZC0,14836
|
|
4
|
+
MatplotLibAPI/Pivot.py,sha256=Bo7ZpkxqoE75e8vpSsKIT5X2Q7lLdfAyy46ox1fARbc,7183
|
|
5
|
+
MatplotLibAPI/Table.py,sha256=hokyBgkiwlA0B8Irx2yPL4xCeZlI-HK0Jln79qjMwZk,1992
|
|
6
|
+
MatplotLibAPI/Timeserie.py,sha256=kl7G5TgIz1MKEBegPE_hHuoPcMCS7-JkCjDyx3wuM8Q,3433
|
|
7
|
+
MatplotLibAPI/Treemap.py,sha256=ImfnPagGQkPwk15Yywroir-FLbBvBzpytEb1Uw7iQi4,2539
|
|
8
|
+
MatplotLibAPI/__init__.py,sha256=FoN0Qa1efBbRDK27vOxdQxT-iRMH4Hqz5WKxMdoSvZc,10430
|
|
9
|
+
MatplotLibAPI-3.1.2.dist-info/LICENSE,sha256=hMErKLb6YZR3lRR5zr-vxeFkvY69QAaafgSpZ5-P1dQ,1067
|
|
10
|
+
MatplotLibAPI-3.1.2.dist-info/METADATA,sha256=uH2EUAne0e9xsrALJ9TUCRnEgfi5P0NIOtsv_4PnVps,462
|
|
11
|
+
MatplotLibAPI-3.1.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
12
|
+
MatplotLibAPI-3.1.2.dist-info/top_level.txt,sha256=MrzbBjDEW48Vb6YhQIqpFYGOhHzQnEIM5Qy2xy2iqew,14
|
|
13
|
+
MatplotLibAPI-3.1.2.dist-info/RECORD,,
|
MatplotLibAPI/Style.py
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
from typing import List, Optional, Union, Dict, Callable
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
7
|
-
import pandas as pd
|
|
8
|
-
|
|
9
|
-
from matplotlib.dates import num2date
|
|
10
|
-
from matplotlib.ticker import FuncFormatter
|
|
11
|
-
|
|
12
|
-
# region Panda
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def _validate_panda(pd_df: pd.DataFrame,
|
|
16
|
-
cols: List[str],
|
|
17
|
-
sort_by: Optional[str] = None):
|
|
18
|
-
_columns = cols.copy()
|
|
19
|
-
if sort_by and sort_by not in _columns:
|
|
20
|
-
_columns.append(sort_by)
|
|
21
|
-
for col in _columns:
|
|
22
|
-
if col not in pd_df.columns:
|
|
23
|
-
raise AttributeError(f"{col} is not a DataFrame's column")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def format_func(
|
|
27
|
-
format_funcs: Optional[Dict[str, Optional[Callable[[Union[int, float, str]], str]]]],
|
|
28
|
-
label: Optional[str] = None,
|
|
29
|
-
x: Optional[str] = None,
|
|
30
|
-
y: Optional[str] = None,
|
|
31
|
-
z: Optional[str] = None):
|
|
32
|
-
|
|
33
|
-
if label and "label" in format_funcs:
|
|
34
|
-
format_funcs[label] = format_funcs["label"]
|
|
35
|
-
if x and "x" in format_funcs:
|
|
36
|
-
format_funcs[x] = format_funcs["x"]
|
|
37
|
-
if y and "y" in format_funcs:
|
|
38
|
-
format_funcs[y] = format_funcs["y"]
|
|
39
|
-
if z and "z" in format_funcs:
|
|
40
|
-
format_funcs[z] = format_funcs["z"]
|
|
41
|
-
return format_funcs
|
|
42
|
-
|
|
43
|
-
# endregion
|
|
44
|
-
|
|
45
|
-
# region Style
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
MAX_RESULTS = 50
|
|
49
|
-
X_COL = "index"
|
|
50
|
-
Y_COL = "overlap"
|
|
51
|
-
Z_COL = "users"
|
|
52
|
-
FIG_SIZE = (19.2, 10.8)
|
|
53
|
-
BACKGROUND_COLOR = 'black'
|
|
54
|
-
TEXT_COLOR = 'white'
|
|
55
|
-
PALETTE = "Greys_r"
|
|
56
|
-
FONT_SIZE = 14
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@dataclass
|
|
60
|
-
class StyleTemplate:
|
|
61
|
-
background_color: str = BACKGROUND_COLOR
|
|
62
|
-
fig_border: str = BACKGROUND_COLOR
|
|
63
|
-
font_name: str = 'Arial'
|
|
64
|
-
font_size: int = FONT_SIZE
|
|
65
|
-
font_color: str = TEXT_COLOR
|
|
66
|
-
palette: str = PALETTE
|
|
67
|
-
legend: bool = True
|
|
68
|
-
xscale: Optional[str] = None
|
|
69
|
-
x_ticks: int = 10
|
|
70
|
-
yscale: Optional[str] = None
|
|
71
|
-
y_ticks: int = 5
|
|
72
|
-
format_funcs: Optional[Dict[str, Optional[Callable[[
|
|
73
|
-
Union[int, float, str]], str]]]] = None
|
|
74
|
-
col_widths: Optional[List[float]] = None
|
|
75
|
-
|
|
76
|
-
@property
|
|
77
|
-
def font_mapping(self):
|
|
78
|
-
return {0: self.font_size-3,
|
|
79
|
-
1: self.font_size-1,
|
|
80
|
-
2: self.font_size,
|
|
81
|
-
3: self.font_size+1,
|
|
82
|
-
4: self.font_size+3}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class DynamicFuncFormatter(FuncFormatter):
|
|
86
|
-
def __init__(self, func_name):
|
|
87
|
-
super().__init__(func_name)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def percent_formatter(val, pos: Optional[int] = None):
|
|
91
|
-
if val*100 <= 0.1: # For 0.1%
|
|
92
|
-
return f"{val*100:.2f}%"
|
|
93
|
-
elif val*100 <= 1: # For 1%
|
|
94
|
-
return f"{val*100:.1f}%"
|
|
95
|
-
else:
|
|
96
|
-
return f"{val*100:.0f}%"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def bmk_formatter(val, pos: Optional[int] = None):
|
|
100
|
-
if val >= 1_000_000_000: # Billions
|
|
101
|
-
return f"{val / 1_000_000_000:.2f}B"
|
|
102
|
-
elif val >= 1_000_000: # Millions
|
|
103
|
-
return f"{val / 1_000_000:.1f}M"
|
|
104
|
-
elif val >= 1_000: # Thousands
|
|
105
|
-
return f"{val / 1_000:.1f}K"
|
|
106
|
-
else:
|
|
107
|
-
return f"{int(val)}"
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def integer_formatter(value, pos: Optional[int] = None):
|
|
111
|
-
return f"{int(value)}"
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def string_formatter(value, pos: Optional[int] = None):
|
|
115
|
-
return str(value).replace("-", " ").replace("_", " ").title()
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def yy_mm__formatter(x, pos: Optional[int] = None):
|
|
119
|
-
return num2date(x).strftime('%Y-%m')
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def yy_mm_dd__formatter(x, pos: Optional[int] = None):
|
|
123
|
-
return num2date(x).strftime('%Y-%m-%D')
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def percent_formatter(x, pos: Optional[int] = None):
|
|
127
|
-
return f"{x * 100:.0f}%"
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def generate_ticks(min_val, max_val, num_ticks="10"):
|
|
131
|
-
# Identify the type of the input
|
|
132
|
-
try:
|
|
133
|
-
min_val = float(min_val)
|
|
134
|
-
max_val = float(max_val)
|
|
135
|
-
is_date = False
|
|
136
|
-
except ValueError:
|
|
137
|
-
is_date = True
|
|
138
|
-
|
|
139
|
-
# Convert string inputs to appropriate numerical or date types
|
|
140
|
-
num_ticks = int(num_ticks)
|
|
141
|
-
|
|
142
|
-
if is_date:
|
|
143
|
-
min_val = pd.Timestamp(min_val).to_datetime64()
|
|
144
|
-
max_val = pd.Timestamp(max_val).to_datetime64()
|
|
145
|
-
data_range = (max_val - min_val).astype('timedelta64[D]').astype(int)
|
|
146
|
-
else:
|
|
147
|
-
data_range = max_val - min_val
|
|
148
|
-
|
|
149
|
-
# Calculate a nice step size
|
|
150
|
-
step_size = data_range / (num_ticks - 1)
|
|
151
|
-
|
|
152
|
-
# If date, convert back to datetime
|
|
153
|
-
if is_date:
|
|
154
|
-
ticks = pd.date_range(
|
|
155
|
-
start=min_val, periods=num_ticks, freq=f"{step_size}D")
|
|
156
|
-
else:
|
|
157
|
-
# Round the step size to a "nice" number
|
|
158
|
-
exponent = np.floor(np.log10(step_size))
|
|
159
|
-
fraction = step_size / 10**exponent
|
|
160
|
-
nice_fraction = round(fraction)
|
|
161
|
-
|
|
162
|
-
# Create nice step size
|
|
163
|
-
nice_step = nice_fraction * 10**exponent
|
|
164
|
-
|
|
165
|
-
# Generate the tick marks based on the nice step size
|
|
166
|
-
ticks = np.arange(min_val, max_val + nice_step, nice_step)
|
|
167
|
-
|
|
168
|
-
return ticks
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# endregion
|
MatplotLibAPI/pdAccessor.py
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import logging
|
|
3
|
-
import warnings
|
|
4
|
-
|
|
5
|
-
from typing import Optional, List
|
|
6
|
-
import pandas as pd
|
|
7
|
-
|
|
8
|
-
from matplotlib.axes import Axes
|
|
9
|
-
from matplotlib.figure import Figure
|
|
10
|
-
import plotly.graph_objects as go
|
|
11
|
-
from .Style import StyleTemplate
|
|
12
|
-
from .Bubble import plot_bubble, BUBBLE_STYLE_TEMPLATE
|
|
13
|
-
from .Composite import plot_composite_bubble
|
|
14
|
-
from .Timeserie import plot_timeserie, TIMESERIE_STYLE_TEMPLATE
|
|
15
|
-
from .Table import plot_table, TABLE_STYLE_TEMPLATE
|
|
16
|
-
from .Network import Graph
|
|
17
|
-
from .Treemap import plot_treemap, TREEMAP_STYLE_TEMPLATE
|
|
18
|
-
|
|
19
|
-
warnings.filterwarnings('ignore')
|
|
20
|
-
logging.getLogger().setLevel(logging.WARNING)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@pd.api.extensions.register_dataframe_accessor("mpl")
|
|
24
|
-
class MatPlotLibAccessor:
|
|
25
|
-
|
|
26
|
-
def __init__(self, pd_df: pd.DataFrame):
|
|
27
|
-
self._obj = pd_df
|
|
28
|
-
|
|
29
|
-
def plot_bubble(self,
|
|
30
|
-
label: str,
|
|
31
|
-
x: str,
|
|
32
|
-
y: str,
|
|
33
|
-
z: str,
|
|
34
|
-
title: Optional[str] = None,
|
|
35
|
-
style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
|
|
36
|
-
max_values: int = 50,
|
|
37
|
-
center_to_mean: bool = False,
|
|
38
|
-
sort_by: Optional[str] = None,
|
|
39
|
-
ascending: bool = False) -> Axes:
|
|
40
|
-
|
|
41
|
-
return plot_bubble(pd_df=self._obj,
|
|
42
|
-
label=label,
|
|
43
|
-
x=x,
|
|
44
|
-
y=y,
|
|
45
|
-
z=z,
|
|
46
|
-
title=title,
|
|
47
|
-
style=style,
|
|
48
|
-
max_values=max_values,
|
|
49
|
-
center_to_mean=center_to_mean,
|
|
50
|
-
sort_by=sort_by,
|
|
51
|
-
ascending=ascending)
|
|
52
|
-
|
|
53
|
-
def plot_composite_bubble(self,
|
|
54
|
-
label: str,
|
|
55
|
-
x: str,
|
|
56
|
-
y: str,
|
|
57
|
-
z: str,
|
|
58
|
-
title: Optional[str] = None,
|
|
59
|
-
style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
|
|
60
|
-
max_values: int = 100,
|
|
61
|
-
center_to_mean: bool = False,
|
|
62
|
-
sort_by: Optional[str] = None,
|
|
63
|
-
ascending: bool = False) -> Figure:
|
|
64
|
-
|
|
65
|
-
return plot_composite_bubble(pd_df=self._obj,
|
|
66
|
-
label=label,
|
|
67
|
-
x=x,
|
|
68
|
-
y=y,
|
|
69
|
-
z=z,
|
|
70
|
-
title=title,
|
|
71
|
-
style=style,
|
|
72
|
-
max_values=max_values,
|
|
73
|
-
center_to_mean=center_to_mean,
|
|
74
|
-
sort_by=sort_by,
|
|
75
|
-
ascending=ascending)
|
|
76
|
-
|
|
77
|
-
def plot_table(self,
|
|
78
|
-
cols: List[str],
|
|
79
|
-
title: Optional[str] = None,
|
|
80
|
-
style: StyleTemplate = TABLE_STYLE_TEMPLATE,
|
|
81
|
-
max_values: int = 20,
|
|
82
|
-
sort_by: Optional[str] = None,
|
|
83
|
-
ascending: bool = False) -> Axes:
|
|
84
|
-
|
|
85
|
-
return plot_table(pd_df=self._obj,
|
|
86
|
-
cols=cols,
|
|
87
|
-
title=title,
|
|
88
|
-
style=style,
|
|
89
|
-
max_values=max_values,
|
|
90
|
-
sort_by=sort_by,
|
|
91
|
-
ascending=ascending)
|
|
92
|
-
|
|
93
|
-
def plot_timeserie(self,
|
|
94
|
-
label: str,
|
|
95
|
-
x: str,
|
|
96
|
-
y: str,
|
|
97
|
-
title: Optional[str] = None,
|
|
98
|
-
style: StyleTemplate = TIMESERIE_STYLE_TEMPLATE,
|
|
99
|
-
max_values: int = 100,
|
|
100
|
-
sort_by: Optional[str] = None,
|
|
101
|
-
ascending: bool = False) -> Axes:
|
|
102
|
-
|
|
103
|
-
return plot_timeserie(pd_df=self._obj,
|
|
104
|
-
label=label,
|
|
105
|
-
x=x,
|
|
106
|
-
y=y,
|
|
107
|
-
title=title,
|
|
108
|
-
style=style,
|
|
109
|
-
max_values=max_values,
|
|
110
|
-
sort_by=sort_by,
|
|
111
|
-
ascending=ascending)
|
|
112
|
-
|
|
113
|
-
def plot_network(self,
|
|
114
|
-
source: str = "source",
|
|
115
|
-
target: str = "target",
|
|
116
|
-
weight: str = "weight",
|
|
117
|
-
title: Optional[str] = None,
|
|
118
|
-
style: StyleTemplate = TIMESERIE_STYLE_TEMPLATE,
|
|
119
|
-
max_values: int = 20,
|
|
120
|
-
sort_by: Optional[str] = None,
|
|
121
|
-
ascending: bool = False) -> Axes:
|
|
122
|
-
|
|
123
|
-
graph = Graph.from_pandas_edgelist(df=self._obj,
|
|
124
|
-
source=source,
|
|
125
|
-
target=target,
|
|
126
|
-
weight=weight)
|
|
127
|
-
|
|
128
|
-
return graph.plotX(title, style)
|
|
129
|
-
|
|
130
|
-
def plot_treemap(self,
|
|
131
|
-
path: str,
|
|
132
|
-
values: str,
|
|
133
|
-
style: StyleTemplate = TREEMAP_STYLE_TEMPLATE,
|
|
134
|
-
title: Optional[str] = None,
|
|
135
|
-
color: Optional[str] = None,
|
|
136
|
-
max_values: int = 100,
|
|
137
|
-
sort_by: Optional[str] = None,
|
|
138
|
-
ascending: bool = False) ->go.Figure:
|
|
139
|
-
return plot_treemap(pd_df=self._obj,
|
|
140
|
-
path=path,
|
|
141
|
-
values=values,
|
|
142
|
-
title=title,
|
|
143
|
-
style=style,
|
|
144
|
-
color=color,
|
|
145
|
-
max_values=max_values,
|
|
146
|
-
sort_by=sort_by,
|
|
147
|
-
ascending=ascending)
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
MatplotLibAPI/Bubble.py,sha256=2PIwL0stzFC7knqdMHBBm99DXPXtBJK-xmpxmVlInYw,3830
|
|
2
|
-
MatplotLibAPI/Composite.py,sha256=M_AaBdn9cfMIOGZBg4_twRS9VU9jbgS6cs9E5l3ObOQ,2763
|
|
3
|
-
MatplotLibAPI/Network.py,sha256=fq1-LfDKy3oC5JzqU7IyZudgR2plmziigjpm2TzEA3U,13286
|
|
4
|
-
MatplotLibAPI/Pivot.py,sha256=8jRYdlvw8otrJ1hYBAaKQJoN84xe-YfroXmgRLykMmQ,7162
|
|
5
|
-
MatplotLibAPI/Style.py,sha256=xtw71VWHSQEvts1nJdKR0c_9Rqef6EGkyyvgdTK5M7c,4632
|
|
6
|
-
MatplotLibAPI/Table.py,sha256=Az5uX-ViqzJPTBym4QnUzn5PD5LEsVzQ9WohcSDFcK4,1990
|
|
7
|
-
MatplotLibAPI/Timeserie.py,sha256=HRFHs_WzaZzLmWVVMBKVMP5M3oglsr1R9Ni53X7_HLI,3432
|
|
8
|
-
MatplotLibAPI/Treemap.py,sha256=diCx5270ENBObO3eWlLlpaHWUWcSgMT34vEwWTxTDAE,1799
|
|
9
|
-
MatplotLibAPI/__init__.py,sha256=PDobJg0sw7BjgSQk7p7kkjombghs3D_m-CCk2oeUt_s,506
|
|
10
|
-
MatplotLibAPI/pdAccessor.py,sha256=5duWsURQKgNuwCz1p0ge2GbAXHSHl8e38zJn6NSVAGg,5745
|
|
11
|
-
MatplotLibAPI-3.1.0.dist-info/LICENSE,sha256=hMErKLb6YZR3lRR5zr-vxeFkvY69QAaafgSpZ5-P1dQ,1067
|
|
12
|
-
MatplotLibAPI-3.1.0.dist-info/METADATA,sha256=UvD4nRviP67nS7KnDWa1Dcmt8PygsjCUs6cioLgHQv4,319
|
|
13
|
-
MatplotLibAPI-3.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
14
|
-
MatplotLibAPI-3.1.0.dist-info/top_level.txt,sha256=MrzbBjDEW48Vb6YhQIqpFYGOhHzQnEIM5Qy2xy2iqew,14
|
|
15
|
-
MatplotLibAPI-3.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|