MatplotLibAPI 3.2.8__tar.gz → 3.2.10__tar.gz
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-3.2.10/MatplotLibAPI/Composite.py +196 -0
- matplotlibapi-3.2.10/MatplotLibAPI/StyleTemplate.py +371 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI/__init__.py +27 -3
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10/MatplotLibAPI.egg-info}/PKG-INFO +1 -1
- {matplotlibapi-3.2.8/MatplotLibAPI.egg-info → matplotlibapi-3.2.10}/PKG-INFO +1 -1
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/pyproject.toml +1 -1
- matplotlibapi-3.2.8/MatplotLibAPI/Composite.py +0 -124
- matplotlibapi-3.2.8/MatplotLibAPI/StyleTemplate.py +0 -169
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/LICENSE +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI/Bubble.py +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI/Network.py +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI/Pivot.py +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI/Table.py +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI/Timeserie.py +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI/Treemap.py +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI.egg-info/SOURCES.txt +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI.egg-info/dependency_links.txt +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI.egg-info/requires.txt +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/MatplotLibAPI.egg-info/top_level.txt +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/README.md +0 -0
- {matplotlibapi-3.2.8 → matplotlibapi-3.2.10}/setup.cfg +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Hint for Visual Code Python Interactive window
|
|
2
|
+
# %%
|
|
3
|
+
from typing import Optional, Tuple, List, Dict
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
from matplotlib.figure import Figure
|
|
7
|
+
import plotly.graph_objects as go
|
|
8
|
+
from plotly.subplots import make_subplots
|
|
9
|
+
from .Bubble import aplot_bubble, BUBBLE_STYLE_TEMPLATE
|
|
10
|
+
from .Table import aplot_table
|
|
11
|
+
from .Treemap import aplot_treemap, TREEMAP_STYLE_TEMPLATE
|
|
12
|
+
from .StyleTemplate import StyleTemplate, format_func, validate_dataframe
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def plot_composite_bubble(
|
|
16
|
+
pd_df: pd.DataFrame,
|
|
17
|
+
label: str,
|
|
18
|
+
x: str,
|
|
19
|
+
y: str,
|
|
20
|
+
z: str,
|
|
21
|
+
title: Optional[str] = "Test",
|
|
22
|
+
style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
|
|
23
|
+
max_values: int = 50,
|
|
24
|
+
center_to_mean: bool = False,
|
|
25
|
+
filter_by: Optional[str] = None,
|
|
26
|
+
sort_by: Optional[str] = None,
|
|
27
|
+
ascending: bool = False,
|
|
28
|
+
table_rows: int = 10,
|
|
29
|
+
figsize: Tuple[float, float] = (19.2, 10.8)) -> Figure:
|
|
30
|
+
"""
|
|
31
|
+
Plot a composite bubble chart with additional tables for top and bottom values.
|
|
32
|
+
|
|
33
|
+
Parameters:
|
|
34
|
+
pd_df (pd.DataFrame):
|
|
35
|
+
The pandas DataFrame containing the data to be plotted.
|
|
36
|
+
label (str):
|
|
37
|
+
The column name for the bubble labels.
|
|
38
|
+
x (str):
|
|
39
|
+
The column name for the x-axis values.
|
|
40
|
+
y (str):
|
|
41
|
+
The column name for the y-axis values.
|
|
42
|
+
z (str):
|
|
43
|
+
The column name for the bubble sizes.
|
|
44
|
+
title (Optional[str]):
|
|
45
|
+
The title of the plot. Default is "Test".
|
|
46
|
+
style (StyleTemplate):
|
|
47
|
+
A StyleTemplate object to define the visual style of the plot. Default is BUBBLE_STYLE_TEMPLATE.
|
|
48
|
+
max_values (int):
|
|
49
|
+
The maximum number of values to display in the bubble chart. Default is 50.
|
|
50
|
+
center_to_mean (bool):
|
|
51
|
+
A flag indicating whether to center the bubbles to the mean. Default is False.
|
|
52
|
+
filter_by (Optional[str]):
|
|
53
|
+
The column name to filter the data by. Default is None.
|
|
54
|
+
sort_by (Optional[str]):
|
|
55
|
+
The column name to sort the data by. Default is None.
|
|
56
|
+
ascending (bool):
|
|
57
|
+
A flag indicating whether to sort in ascending order. Default is False.
|
|
58
|
+
table_rows (int):
|
|
59
|
+
The number of rows to display in the tables. Default is 10.
|
|
60
|
+
figsize (Tuple[float, float]):
|
|
61
|
+
The size of the figure. Default is (19.2, 10.8).
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Figure:
|
|
65
|
+
The matplotlib figure object containing the composite bubble chart and tables.
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
>>> df = pd.DataFrame({
|
|
69
|
+
... "Label": ["A", "B", "C"],
|
|
70
|
+
... "X": [1, 2, 3],
|
|
71
|
+
... "Y": [4, 5, 6],
|
|
72
|
+
... "Z": [7, 8, 9]
|
|
73
|
+
... })
|
|
74
|
+
>>> fig = plot_composite_bubble(df, label="Label", x="X", y="Y", z="Z")
|
|
75
|
+
>>> fig.show()
|
|
76
|
+
"""
|
|
77
|
+
validate_dataframe(pd_df, cols=[label, x, y, z], sort_by=sort_by)
|
|
78
|
+
|
|
79
|
+
if not sort_by:
|
|
80
|
+
sort_by = z
|
|
81
|
+
if not filter_by:
|
|
82
|
+
filter_by = z
|
|
83
|
+
plot_df = pd_df.sort_values(by=filter_by,
|
|
84
|
+
ascending=ascending)[[label, x, y, z]].head(max_values)
|
|
85
|
+
style.format_funcs = format_func(
|
|
86
|
+
style.format_funcs, label=label, x=x, y=y, z=z)
|
|
87
|
+
fig = plt.figure(figsize=figsize)
|
|
88
|
+
fig.patch.set_facecolor(style.background_color)
|
|
89
|
+
grid = plt.GridSpec(2, 2, height_ratios=[2, 1], width_ratios=[1, 1])
|
|
90
|
+
ax = fig.add_subplot(grid[0, 0:])
|
|
91
|
+
ax = aplot_bubble(pd_df=plot_df,
|
|
92
|
+
label=label,
|
|
93
|
+
x=x,
|
|
94
|
+
y=y,
|
|
95
|
+
z=z,
|
|
96
|
+
title=title,
|
|
97
|
+
style=style,
|
|
98
|
+
max_values=max_values,
|
|
99
|
+
center_to_mean=center_to_mean,
|
|
100
|
+
sort_by=sort_by,
|
|
101
|
+
ascending=ascending,
|
|
102
|
+
ax=ax)
|
|
103
|
+
|
|
104
|
+
if "label" in style.format_funcs:
|
|
105
|
+
style.format_funcs[label] = style.format_funcs["label"]
|
|
106
|
+
if "x" in style.format_funcs:
|
|
107
|
+
style.format_funcs[x] = style.format_funcs["x"]
|
|
108
|
+
if "y" in style.format_funcs:
|
|
109
|
+
style.format_funcs[y] = style.format_funcs["y"]
|
|
110
|
+
if "z" in style.format_funcs:
|
|
111
|
+
style.format_funcs[z] = style.format_funcs["z"]
|
|
112
|
+
|
|
113
|
+
ax2 = fig.add_subplot(grid[1, 0])
|
|
114
|
+
ax2 = aplot_table(
|
|
115
|
+
pd_df=plot_df,
|
|
116
|
+
cols=[label, z, y, x],
|
|
117
|
+
title=f"Top {table_rows}",
|
|
118
|
+
ax=ax2,
|
|
119
|
+
sort_by=sort_by,
|
|
120
|
+
ascending=False,
|
|
121
|
+
max_values=table_rows,
|
|
122
|
+
style=style
|
|
123
|
+
)
|
|
124
|
+
ax3 = fig.add_subplot(grid[1, 1])
|
|
125
|
+
ax3 = aplot_table(
|
|
126
|
+
pd_df=plot_df,
|
|
127
|
+
cols=[label, z, y, x],
|
|
128
|
+
title=f"Last {table_rows}",
|
|
129
|
+
ax=ax3,
|
|
130
|
+
sort_by=sort_by,
|
|
131
|
+
ascending=True,
|
|
132
|
+
max_values=table_rows,
|
|
133
|
+
style=style
|
|
134
|
+
)
|
|
135
|
+
fig.tight_layout()
|
|
136
|
+
return fig
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def plot_composite_treemap(pd_dfs: Dict[str, pd.DataFrame],
|
|
140
|
+
values: str,
|
|
141
|
+
style: StyleTemplate = TREEMAP_STYLE_TEMPLATE,
|
|
142
|
+
title: Optional[str] = None,
|
|
143
|
+
color: Optional[str] = None,
|
|
144
|
+
sort_by: Optional[str] = None,
|
|
145
|
+
ascending: bool = False,
|
|
146
|
+
max_values: int = 100) -> Optional[go.Figure]:
|
|
147
|
+
"""
|
|
148
|
+
Plot a composite treemap from multiple DataFrames.
|
|
149
|
+
|
|
150
|
+
Parameters:
|
|
151
|
+
pd_dfs (Dict[str, pd.DataFrame]):
|
|
152
|
+
A dictionary where keys are dimension names and values are pandas DataFrames to be plotted.
|
|
153
|
+
values (str):
|
|
154
|
+
The column name in each DataFrame that contains the values to be visualized in the treemap.
|
|
155
|
+
style (StyleTemplate):
|
|
156
|
+
A StyleTemplate object to define the visual style of the treemap. Default is TREEMAP_STYLE_TEMPLATE.
|
|
157
|
+
title (Optional[str]):
|
|
158
|
+
An optional title for the composite treemap plot. Default is None.
|
|
159
|
+
color (Optional[str]):
|
|
160
|
+
An optional column name for coloring the treemap. Default is None.
|
|
161
|
+
sort_by (Optional[str]):
|
|
162
|
+
An optional column name for sorting the treemap values. Default is None.
|
|
163
|
+
ascending (bool):
|
|
164
|
+
A flag indicating whether to sort in ascending order. Default is False.
|
|
165
|
+
max_values (int):
|
|
166
|
+
The maximum number of values to display in each treemap. Default is 100.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Optional[go.Figure]:
|
|
170
|
+
The composite treemap figure object or None if no DataFrames are provided.
|
|
171
|
+
"""
|
|
172
|
+
num_dimensions = len(pd_dfs)
|
|
173
|
+
if num_dimensions > 0:
|
|
174
|
+
subplot_titles = [f"{title}::{dim.title()}" if title is not None else dim.title(
|
|
175
|
+
) for dim in pd_dfs.keys()]
|
|
176
|
+
fig = make_subplots(
|
|
177
|
+
rows=num_dimensions,
|
|
178
|
+
cols=1,
|
|
179
|
+
specs = [[{"type": "treemap"} for _ in range(0, 1)] for _ in range(0, num_dimensions)],
|
|
180
|
+
subplot_titles=subplot_titles,
|
|
181
|
+
vertical_spacing=0.2
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
current_row = 1
|
|
185
|
+
for path, df in pd_dfs.items():
|
|
186
|
+
trm = aplot_treemap(pd_df=df,
|
|
187
|
+
path=path,
|
|
188
|
+
values=values,
|
|
189
|
+
style=style,
|
|
190
|
+
color=color,
|
|
191
|
+
sort_by=sort_by,
|
|
192
|
+
ascending=ascending,
|
|
193
|
+
max_values=max_values)
|
|
194
|
+
fig.add_trace(trm, row=current_row, col=1)
|
|
195
|
+
current_row += 1
|
|
196
|
+
return fig
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional, Dict, Callable, Union
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from matplotlib.dates import num2date
|
|
9
|
+
from matplotlib.ticker import FuncFormatter
|
|
10
|
+
|
|
11
|
+
# region Utils
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def validate_dataframe(pd_df: pd.DataFrame,
|
|
15
|
+
cols: List[str],
|
|
16
|
+
sort_by: Optional[str] = None):
|
|
17
|
+
"""
|
|
18
|
+
Validate that specified columns exist in a pandas DataFrame and optionally check for a sorting column.
|
|
19
|
+
|
|
20
|
+
Parameters:
|
|
21
|
+
pd_df (pd.DataFrame): The pandas DataFrame to validate.
|
|
22
|
+
cols (List[str]): A list of column names that must exist in the DataFrame.
|
|
23
|
+
sort_by (Optional[str]): An optional column name that, if provided, must also exist in the DataFrame.
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
AttributeError: If any of the specified columns or the sorting column (if provided) do not exist in the DataFrame.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
>>> import pandas as pd
|
|
30
|
+
>>> data = {'A': [1, 2, 3], 'B': [4, 5, 6]}
|
|
31
|
+
>>> df = pd.DataFrame(data)
|
|
32
|
+
>>> validate_dataframe(df, ['A', 'B']) # No error
|
|
33
|
+
>>> validate_dataframe(df, ['A', 'C']) # Raises AttributeError
|
|
34
|
+
>>> validate_dataframe(df, ['A'], sort_by='B') # No error
|
|
35
|
+
>>> validate_dataframe(df, ['A'], sort_by='C') # Raises AttributeError
|
|
36
|
+
"""
|
|
37
|
+
_columns = cols.copy()
|
|
38
|
+
if sort_by and sort_by not in _columns:
|
|
39
|
+
_columns.append(sort_by)
|
|
40
|
+
for col in _columns:
|
|
41
|
+
if col not in pd_df.columns:
|
|
42
|
+
raise AttributeError(f"{col} is not a DataFrame's column")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def format_func(
|
|
46
|
+
format_funcs: Optional[Dict[str, Optional[Callable[[Union[int, float, str]], str]]]],
|
|
47
|
+
label: Optional[str] = None,
|
|
48
|
+
x: Optional[str] = None,
|
|
49
|
+
y: Optional[str] = None,
|
|
50
|
+
z: Optional[str] = None):
|
|
51
|
+
"""
|
|
52
|
+
Update the formatting functions for specified keys if they exist in the provided format functions dictionary.
|
|
53
|
+
|
|
54
|
+
Parameters:
|
|
55
|
+
format_funcs (Optional[Dict[str, Optional[Callable[[Union[int, float, str]], str]]]]):
|
|
56
|
+
A dictionary mapping keys to formatting functions. The keys can be 'label', 'x', 'y', and 'z'.
|
|
57
|
+
label (Optional[str]):
|
|
58
|
+
The key to update with the 'label' formatting function from the dictionary.
|
|
59
|
+
x (Optional[str]):
|
|
60
|
+
The key to update with the 'x' formatting function from the dictionary.
|
|
61
|
+
y (Optional[str]):
|
|
62
|
+
The key to update with the 'y' formatting function from the dictionary.
|
|
63
|
+
z (Optional[str]):
|
|
64
|
+
The key to update with the 'z' formatting function from the dictionary.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Optional[Dict[str, Optional[Callable[[Union[int, float, str]], str]]]]:
|
|
68
|
+
The updated dictionary with the specified keys pointing to their corresponding formatting functions.
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
>>> format_funcs = {
|
|
72
|
+
... "label": lambda x: f"Label: {x}",
|
|
73
|
+
... "x": lambda x: f"X-axis: {x}",
|
|
74
|
+
... "y": lambda y: f"Y-axis: {y}",
|
|
75
|
+
... }
|
|
76
|
+
>>> updated_funcs = format_func(format_funcs, label="new_label", x="new_x")
|
|
77
|
+
>>> print(updated_funcs)
|
|
78
|
+
{
|
|
79
|
+
"label": lambda x: f"Label: {x}",
|
|
80
|
+
"x": lambda x: f"X-axis: {x}",
|
|
81
|
+
"y": lambda y: f"Y-axis: {y}",
|
|
82
|
+
"new_label": lambda x: f"Label: {x}",
|
|
83
|
+
"new_x": lambda x: f"X-axis: {x}",
|
|
84
|
+
}
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
if label and "label" in format_funcs:
|
|
88
|
+
format_funcs[label] = format_funcs["label"]
|
|
89
|
+
if x and "x" in format_funcs:
|
|
90
|
+
format_funcs[x] = format_funcs["x"]
|
|
91
|
+
if y and "y" in format_funcs:
|
|
92
|
+
format_funcs[y] = format_funcs["y"]
|
|
93
|
+
if z and "z" in format_funcs:
|
|
94
|
+
format_funcs[z] = format_funcs["z"]
|
|
95
|
+
return format_funcs
|
|
96
|
+
|
|
97
|
+
# endregion
|
|
98
|
+
|
|
99
|
+
# region Style
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
MAX_RESULTS = 50
|
|
103
|
+
X_COL = "index"
|
|
104
|
+
Y_COL = "overlap"
|
|
105
|
+
Z_COL = "users"
|
|
106
|
+
FIG_SIZE = (19.2, 10.8)
|
|
107
|
+
BACKGROUND_COLOR = 'black'
|
|
108
|
+
TEXT_COLOR = 'white'
|
|
109
|
+
PALETTE = "Greys_r"
|
|
110
|
+
FONT_SIZE = 14
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass
|
|
114
|
+
class StyleTemplate:
|
|
115
|
+
background_color: str = BACKGROUND_COLOR
|
|
116
|
+
fig_border: str = BACKGROUND_COLOR
|
|
117
|
+
font_name: str = 'Arial'
|
|
118
|
+
font_size: int = FONT_SIZE
|
|
119
|
+
font_color: str = TEXT_COLOR
|
|
120
|
+
palette: str = PALETTE
|
|
121
|
+
legend: bool = True
|
|
122
|
+
xscale: Optional[str] = None
|
|
123
|
+
x_ticks: int = 5
|
|
124
|
+
yscale: Optional[str] = None
|
|
125
|
+
y_ticks: int = 5
|
|
126
|
+
format_funcs: Optional[Dict[str, Optional[Callable[[
|
|
127
|
+
Union[int, float, str]], str]]]] = None
|
|
128
|
+
col_widths: Optional[List[float]] = None
|
|
129
|
+
"""
|
|
130
|
+
A class to define style templates for data visualization with customizable attributes.
|
|
131
|
+
|
|
132
|
+
Attributes:
|
|
133
|
+
background_color (str):
|
|
134
|
+
The background color for the visualizations. Default is BACKGROUND_COLOR.
|
|
135
|
+
fig_border (str):
|
|
136
|
+
The border color for the figures. Default is BACKGROUND_COLOR.
|
|
137
|
+
font_name (str):
|
|
138
|
+
The name of the font to use. Default is 'Arial'.
|
|
139
|
+
font_size (int):
|
|
140
|
+
The base size of the font. Default is FONT_SIZE.
|
|
141
|
+
font_color (str):
|
|
142
|
+
The color of the font. Default is TEXT_COLOR.
|
|
143
|
+
palette (str):
|
|
144
|
+
The color palette to use. Default is PALETTE.
|
|
145
|
+
legend (bool):
|
|
146
|
+
A flag to determine if the legend should be displayed. Default is True.
|
|
147
|
+
xscale (Optional[str]):
|
|
148
|
+
The scale type for the x-axis. Default is None.
|
|
149
|
+
x_ticks (int):
|
|
150
|
+
The number of ticks on the x-axis. Default is 10.
|
|
151
|
+
yscale (Optional[str]):
|
|
152
|
+
The scale type for the y-axis. Default is None.
|
|
153
|
+
y_ticks (int):
|
|
154
|
+
The number of ticks on the y-axis. Default is 5.
|
|
155
|
+
format_funcs (Optional[Dict[str, Optional[Callable[[Union[int, float, str]], str]]]]):
|
|
156
|
+
A dictionary mapping data keys to formatting functions. Default is None.
|
|
157
|
+
col_widths (Optional[List[float]]):
|
|
158
|
+
A list of column widths. Default is None.
|
|
159
|
+
|
|
160
|
+
Properties:
|
|
161
|
+
font_mapping (dict):
|
|
162
|
+
A dictionary mapping font size levels to specific font sizes.
|
|
163
|
+
|
|
164
|
+
Example:
|
|
165
|
+
>>> template = StyleTemplate()
|
|
166
|
+
>>> template.font_mapping
|
|
167
|
+
{0: FONT_SIZE-3, 1: FONT_SIZE-1, 2: FONT_SIZE, 3: FONT_SIZE+1, 4: FONT_SIZE+3}
|
|
168
|
+
"""
|
|
169
|
+
@property
|
|
170
|
+
def font_mapping(self):
|
|
171
|
+
return {0: self.font_size-3,
|
|
172
|
+
1: self.font_size-1,
|
|
173
|
+
2: self.font_size,
|
|
174
|
+
3: self.font_size+1,
|
|
175
|
+
4: self.font_size+3}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class DynamicFuncFormatter(FuncFormatter):
|
|
179
|
+
"""
|
|
180
|
+
A class to create a dynamic function formatter for matplotlib plots.
|
|
181
|
+
|
|
182
|
+
Inherits from:
|
|
183
|
+
FuncFormatter: A base class from matplotlib for formatting axis ticks.
|
|
184
|
+
|
|
185
|
+
Parameters:
|
|
186
|
+
func_name (Callable): The function to be used for formatting.
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
>>> formatter = DynamicFuncFormatter(percent_formatter)
|
|
190
|
+
"""
|
|
191
|
+
def __init__(self, func_name):
|
|
192
|
+
super().__init__(func_name)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def percent_formatter(val, pos: Optional[int] = None):
|
|
196
|
+
"""
|
|
197
|
+
Format a value as a percentage.
|
|
198
|
+
|
|
199
|
+
Parameters:
|
|
200
|
+
val (float): The value to format.
|
|
201
|
+
pos (Optional[int]): The position (not used).
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
str: The formatted percentage string.
|
|
205
|
+
|
|
206
|
+
Example:
|
|
207
|
+
>>> percent_formatter(0.005)
|
|
208
|
+
'1%'
|
|
209
|
+
"""
|
|
210
|
+
if val*100 <= 0.1: # For 0.1%
|
|
211
|
+
return f"{val*100:.2f}%"
|
|
212
|
+
elif val*100 <= 1: # For 1%
|
|
213
|
+
return f"{val*100:.1f}%"
|
|
214
|
+
else:
|
|
215
|
+
return f"{val*100:.0f}%"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def bmk_formatter(val, pos: Optional[int] = None):
|
|
219
|
+
"""
|
|
220
|
+
Format a value as billions, millions, or thousands.
|
|
221
|
+
|
|
222
|
+
Parameters:
|
|
223
|
+
val (float): The value to format.
|
|
224
|
+
pos (Optional[int]): The position (not used).
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
str: The formatted string with B, M, or K suffix.
|
|
228
|
+
|
|
229
|
+
Example:
|
|
230
|
+
>>> bmk_formatter(1500000)
|
|
231
|
+
'1.5M'
|
|
232
|
+
"""
|
|
233
|
+
if val >= 1_000_000_000: # Billions
|
|
234
|
+
return f"{val / 1_000_000_000:.2f}B"
|
|
235
|
+
elif val >= 1_000_000: # Millions
|
|
236
|
+
return f"{val / 1_000_000:.1f}M"
|
|
237
|
+
elif val >= 1_000: # Thousands
|
|
238
|
+
return f"{val / 1_000:.1f}K"
|
|
239
|
+
else:
|
|
240
|
+
return f"{int(val)}"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def integer_formatter(value, pos: Optional[int] = None):
|
|
244
|
+
"""
|
|
245
|
+
Format a value as an integer.
|
|
246
|
+
|
|
247
|
+
Parameters:
|
|
248
|
+
value (float): The value to format.
|
|
249
|
+
pos (Optional[int]): The position (not used).
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
str: The formatted integer string.
|
|
253
|
+
|
|
254
|
+
Example:
|
|
255
|
+
>>> integer_formatter(42.9)
|
|
256
|
+
'42'
|
|
257
|
+
"""
|
|
258
|
+
return f"{int(value)}"
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def string_formatter(value, pos: Optional[int] = None):
|
|
262
|
+
"""
|
|
263
|
+
Format a string by replacing '-' and '_' with spaces and capitalizing words.
|
|
264
|
+
|
|
265
|
+
Parameters:
|
|
266
|
+
value (str): The string to format.
|
|
267
|
+
pos (Optional[int]): The position (not used).
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
str: The formatted string.
|
|
271
|
+
|
|
272
|
+
Example:
|
|
273
|
+
>>> string_formatter("example-string_formatter")
|
|
274
|
+
'Example String Formatter'
|
|
275
|
+
"""
|
|
276
|
+
return str(value).replace("-", " ").replace("_", " ").title()
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def yy_mm__formatter(x, pos: Optional[int] = None):
|
|
280
|
+
"""
|
|
281
|
+
Format a date as 'YYYY-MM'.
|
|
282
|
+
|
|
283
|
+
Parameters:
|
|
284
|
+
x (float): The value to format.
|
|
285
|
+
pos (Optional[int]): The position (not used).
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
str: The formatted date string.
|
|
289
|
+
|
|
290
|
+
Example:
|
|
291
|
+
>>> yy_mm__formatter(737060)
|
|
292
|
+
'2020-01'
|
|
293
|
+
"""
|
|
294
|
+
return num2date(x).strftime('%Y-%m')
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def yy_mm_dd__formatter(x, pos: Optional[int] = None):
|
|
298
|
+
"""
|
|
299
|
+
Format a date as 'YYYY-MM-DD'.
|
|
300
|
+
|
|
301
|
+
Parameters:
|
|
302
|
+
x (float): The value to format.
|
|
303
|
+
pos (Optional[int]): The position (not used).
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
str: The formatted date string.
|
|
307
|
+
|
|
308
|
+
Example:
|
|
309
|
+
>>> yy_mm_dd__formatter(737060)
|
|
310
|
+
'2020-01-01'
|
|
311
|
+
"""
|
|
312
|
+
return num2date(x).strftime('%Y-%m-%D')
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def generate_ticks(min_val, max_val, num_ticks:int=5):
|
|
316
|
+
"""
|
|
317
|
+
Generate tick marks for a given range.
|
|
318
|
+
|
|
319
|
+
Parameters:
|
|
320
|
+
min_val (Union[float, str]): The minimum value of the range.
|
|
321
|
+
max_val (Union[float, str]): The maximum value of the range.
|
|
322
|
+
num_ticks (int): The number of ticks to generate. Default is 10.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
np.ndarray: An array of tick marks.
|
|
326
|
+
|
|
327
|
+
Example:
|
|
328
|
+
>>> generate_ticks(0, 100, 5)
|
|
329
|
+
array([ 0., 25., 50., 75., 100.])
|
|
330
|
+
"""
|
|
331
|
+
# Identify the type of the input
|
|
332
|
+
try:
|
|
333
|
+
min_val = float(min_val)
|
|
334
|
+
max_val = float(max_val)
|
|
335
|
+
is_date = False
|
|
336
|
+
except ValueError:
|
|
337
|
+
is_date = True
|
|
338
|
+
|
|
339
|
+
# Convert string inputs to appropriate numerical or date types
|
|
340
|
+
num_ticks = int(num_ticks)
|
|
341
|
+
|
|
342
|
+
if is_date:
|
|
343
|
+
min_val = pd.Timestamp(min_val).to_datetime64()
|
|
344
|
+
max_val = pd.Timestamp(max_val).to_datetime64()
|
|
345
|
+
data_range = (max_val - min_val).astype('timedelta64[D]').astype(int)
|
|
346
|
+
else:
|
|
347
|
+
data_range = max_val - min_val
|
|
348
|
+
|
|
349
|
+
# Calculate a nice step size
|
|
350
|
+
step_size = data_range / (num_ticks - 1)
|
|
351
|
+
|
|
352
|
+
# If date, convert back to datetime
|
|
353
|
+
if is_date:
|
|
354
|
+
ticks = pd.date_range(
|
|
355
|
+
start=min_val, periods=num_ticks, freq=f"{step_size}D")
|
|
356
|
+
else:
|
|
357
|
+
# Round the step size to a "nice" number
|
|
358
|
+
exponent = np.floor(np.log10(step_size))
|
|
359
|
+
fraction = step_size / 10**exponent
|
|
360
|
+
nice_fraction = round(fraction)
|
|
361
|
+
|
|
362
|
+
# Create nice step size
|
|
363
|
+
nice_step = nice_fraction * 10**exponent
|
|
364
|
+
|
|
365
|
+
# Generate the tick marks based on the nice step size
|
|
366
|
+
ticks = np.arange(min_val, max_val + nice_step, nice_step)
|
|
367
|
+
|
|
368
|
+
return ticks
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
# endregion
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
|
|
2
2
|
from .StyleTemplate import StyleTemplate
|
|
3
3
|
from .Bubble import aplot_bubble, fplot_bubble, BUBBLE_STYLE_TEMPLATE
|
|
4
|
-
from .Composite import plot_composite_bubble
|
|
4
|
+
from .Composite import plot_composite_bubble,plot_composite_treemap
|
|
5
5
|
from .Timeserie import aplot_timeserie, fplot_timeserie, TIMESERIE_STYLE_TEMPLATE
|
|
6
6
|
from .Table import aplot_table, fplot_table, TABLE_STYLE_TEMPLATE
|
|
7
7
|
from .Network import aplot_network, aplot_network_components, fplot_network, NETWORK_STYLE_TEMPLATE
|
|
8
8
|
from .Treemap import fplot_treemap, aplot_treemap, TREEMAP_STYLE_TEMPLATE
|
|
9
|
-
from typing import List, Optional, Tuple
|
|
9
|
+
from typing import List, Optional, Tuple,Dict
|
|
10
10
|
import pandas as pd
|
|
11
11
|
from pandas.api.extensions import register_dataframe_accessor
|
|
12
12
|
|
|
@@ -294,6 +294,30 @@ class DataFrameAccessor:
|
|
|
294
294
|
ascending=ascending,
|
|
295
295
|
max_values=max_values)
|
|
296
296
|
|
|
297
|
+
def fplot_composite_treemap(self,
|
|
298
|
+
pathes: List[str],
|
|
299
|
+
values: str,
|
|
300
|
+
style: StyleTemplate = TREEMAP_STYLE_TEMPLATE,
|
|
301
|
+
title: Optional[str] = None,
|
|
302
|
+
color: Optional[str] = None,
|
|
303
|
+
sort_by: Optional[str] = None,
|
|
304
|
+
max_values: int = 100,
|
|
305
|
+
ascending: bool = False,
|
|
306
|
+
fig: Optional[go.Figure] = None) -> go.Figure:
|
|
307
|
+
pd_dfs:Dict[str,pd.DataFrame]={}
|
|
308
|
+
for path in pathes:
|
|
309
|
+
pd_dfs[path]=self._obj
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
return plot_composite_treemap(pd_dfs=pd_dfs,
|
|
313
|
+
values=values,
|
|
314
|
+
title=title,
|
|
315
|
+
style=style,
|
|
316
|
+
color=color,
|
|
317
|
+
sort_by=sort_by,
|
|
318
|
+
ascending=ascending,
|
|
319
|
+
max_values=max_values)
|
|
320
|
+
|
|
297
321
|
|
|
298
322
|
__all__ = ["validate_dataframe", "aplot_bubble", "aplot_timeserie", "aplot_table", "aplot_network", "aplot_network_components", "fplot_network",
|
|
299
|
-
"plot_pivotbar", "fplot_treemap", "aplot_treemap", "plot_composite_bubble", "StyleTemplate", "DataFrameAccessor"]
|
|
323
|
+
"plot_pivotbar", "fplot_treemap", "aplot_treemap", "plot_composite_bubble","plot_composite_treemap", "StyleTemplate", "DataFrameAccessor"]
|
|
@@ -3,7 +3,7 @@ requires = ["setuptools", "wheel"]
|
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
[project]
|
|
5
5
|
name = "MatplotLibAPI"
|
|
6
|
-
version="v3.2.
|
|
6
|
+
version="v3.2.10"
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
requires-python=">=3.7"
|
|
9
9
|
dependencies = ["pandas","matplotlib","networkx","plotly","seaborn","scikit-learn","kaleido","nbformat"]
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# Hint for Visual Code Python Interactive window
|
|
2
|
-
# %%
|
|
3
|
-
from typing import Optional, Tuple, List
|
|
4
|
-
import pandas as pd
|
|
5
|
-
import matplotlib.pyplot as plt
|
|
6
|
-
from matplotlib.figure import Figure
|
|
7
|
-
import plotly.graph_objects as go
|
|
8
|
-
from plotly.subplots import make_subplots
|
|
9
|
-
from .Bubble import aplot_bubble, BUBBLE_STYLE_TEMPLATE
|
|
10
|
-
from .Table import aplot_table
|
|
11
|
-
from .Treemap import aplot_treemap, TREEMAP_STYLE_TEMPLATE
|
|
12
|
-
from .StyleTemplate import StyleTemplate, format_func, validate_dataframe
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def plot_composite_bubble(
|
|
16
|
-
pd_df: pd.DataFrame,
|
|
17
|
-
label: str,
|
|
18
|
-
x: str,
|
|
19
|
-
y: str,
|
|
20
|
-
z: str,
|
|
21
|
-
title: Optional[str] = "Test",
|
|
22
|
-
style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
|
|
23
|
-
max_values: int = 50,
|
|
24
|
-
center_to_mean: bool = False,
|
|
25
|
-
filter_by: Optional[str] = None,
|
|
26
|
-
sort_by: Optional[str] = None,
|
|
27
|
-
ascending: bool = False,
|
|
28
|
-
table_rows: int = 10,
|
|
29
|
-
figsize: Tuple[float, float] = (19.2, 10.8)) -> Figure:
|
|
30
|
-
|
|
31
|
-
validate_dataframe(pd_df, cols=[label, x, y, z], sort_by=sort_by)
|
|
32
|
-
|
|
33
|
-
if not sort_by:
|
|
34
|
-
sort_by = z
|
|
35
|
-
if not filter_by:
|
|
36
|
-
filter_by = z
|
|
37
|
-
plot_df = pd_df.sort_values(by=filter_by,
|
|
38
|
-
ascending=ascending)[[label, x, y, z]].head(max_values)
|
|
39
|
-
style.format_funcs = format_func(
|
|
40
|
-
style.format_funcs, label=label, x=x, y=y, z=z)
|
|
41
|
-
fig = plt.figure(figsize=figsize)
|
|
42
|
-
fig.patch.set_facecolor(style.background_color)
|
|
43
|
-
grid = plt.GridSpec(2, 2, height_ratios=[2, 1], width_ratios=[1, 1])
|
|
44
|
-
ax = fig.add_subplot(grid[0, 0:])
|
|
45
|
-
ax = aplot_bubble(pd_df=plot_df,
|
|
46
|
-
label=label,
|
|
47
|
-
x=x,
|
|
48
|
-
y=y,
|
|
49
|
-
z=z,
|
|
50
|
-
title=title,
|
|
51
|
-
style=style,
|
|
52
|
-
max_values=max_values,
|
|
53
|
-
center_to_mean=center_to_mean,
|
|
54
|
-
sort_by=sort_by,
|
|
55
|
-
ascending=ascending,
|
|
56
|
-
ax=ax)
|
|
57
|
-
|
|
58
|
-
if "label" in style.format_funcs:
|
|
59
|
-
style.format_funcs[label] = style.format_funcs["label"]
|
|
60
|
-
if "x" in style.format_funcs:
|
|
61
|
-
style.format_funcs[x] = style.format_funcs["x"]
|
|
62
|
-
if "y" in style.format_funcs:
|
|
63
|
-
style.format_funcs[y] = style.format_funcs["y"]
|
|
64
|
-
if "z" in style.format_funcs:
|
|
65
|
-
style.format_funcs[z] = style.format_funcs["z"]
|
|
66
|
-
|
|
67
|
-
ax2 = fig.add_subplot(grid[1, 0])
|
|
68
|
-
ax2 = aplot_table(
|
|
69
|
-
pd_df=plot_df,
|
|
70
|
-
cols=[label, z, y, x],
|
|
71
|
-
title=f"Top {table_rows}",
|
|
72
|
-
ax=ax2,
|
|
73
|
-
sort_by=sort_by,
|
|
74
|
-
ascending=False,
|
|
75
|
-
max_values=table_rows,
|
|
76
|
-
style=style
|
|
77
|
-
)
|
|
78
|
-
ax3 = fig.add_subplot(grid[1, 1])
|
|
79
|
-
ax3 = aplot_table(
|
|
80
|
-
pd_df=plot_df,
|
|
81
|
-
cols=[label, z, y, x],
|
|
82
|
-
title=f"Last {table_rows}",
|
|
83
|
-
ax=ax3,
|
|
84
|
-
sort_by=sort_by,
|
|
85
|
-
ascending=True,
|
|
86
|
-
max_values=table_rows,
|
|
87
|
-
style=style
|
|
88
|
-
)
|
|
89
|
-
fig.tight_layout()
|
|
90
|
-
return fig
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def plot_composite_treemap(pd_dfs: List[pd.DataFrame],
|
|
94
|
-
path: str,
|
|
95
|
-
values: str,
|
|
96
|
-
style: StyleTemplate = TREEMAP_STYLE_TEMPLATE,
|
|
97
|
-
title: Optional[str] = None,
|
|
98
|
-
color: Optional[str] = None,
|
|
99
|
-
sort_by: Optional[str] = None,
|
|
100
|
-
ascending: bool = False,
|
|
101
|
-
max_values: int = 100) -> go.Figure:
|
|
102
|
-
|
|
103
|
-
num_dimensions = len(pd_dfs)
|
|
104
|
-
if num_dimensions > 0:
|
|
105
|
-
fig = make_subplots(
|
|
106
|
-
rows=num_dimensions,
|
|
107
|
-
cols=1,
|
|
108
|
-
specs=[[{'type': 'domain'}] for _ in range(num_dimensions)],
|
|
109
|
-
vertical_spacing=0.02
|
|
110
|
-
)
|
|
111
|
-
current_row = 1
|
|
112
|
-
for pd_df in pd_dfs:
|
|
113
|
-
trm = aplot_treemap(pd_df=pd_df,
|
|
114
|
-
path=path,
|
|
115
|
-
values=values,
|
|
116
|
-
title=title,
|
|
117
|
-
style=style,
|
|
118
|
-
color=color,
|
|
119
|
-
sort_by=sort_by,
|
|
120
|
-
ascending=ascending,
|
|
121
|
-
max_values=max_values)
|
|
122
|
-
fig.add_trace(trm,row=current_row, col=1)
|
|
123
|
-
current_row += 1
|
|
124
|
-
return fig
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
from typing import List, Optional, Dict, Callable, Union
|
|
4
|
-
from dataclasses import dataclass
|
|
5
|
-
import pandas as pd
|
|
6
|
-
import numpy as np
|
|
7
|
-
|
|
8
|
-
from matplotlib.dates import num2date
|
|
9
|
-
from matplotlib.ticker import FuncFormatter
|
|
10
|
-
|
|
11
|
-
# region Utils
|
|
12
|
-
|
|
13
|
-
def validate_dataframe(pd_df: pd.DataFrame,
|
|
14
|
-
cols: List[str],
|
|
15
|
-
sort_by: Optional[str] = None):
|
|
16
|
-
_columns = cols.copy()
|
|
17
|
-
if sort_by and sort_by not in _columns:
|
|
18
|
-
_columns.append(sort_by)
|
|
19
|
-
for col in _columns:
|
|
20
|
-
if col not in pd_df.columns:
|
|
21
|
-
raise AttributeError(f"{col} is not a DataFrame's column")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def format_func(
|
|
25
|
-
format_funcs: Optional[Dict[str, Optional[Callable[[Union[int, float, str]], str]]]],
|
|
26
|
-
label: Optional[str] = None,
|
|
27
|
-
x: Optional[str] = None,
|
|
28
|
-
y: Optional[str] = None,
|
|
29
|
-
z: Optional[str] = None):
|
|
30
|
-
|
|
31
|
-
if label and "label" in format_funcs:
|
|
32
|
-
format_funcs[label] = format_funcs["label"]
|
|
33
|
-
if x and "x" in format_funcs:
|
|
34
|
-
format_funcs[x] = format_funcs["x"]
|
|
35
|
-
if y and "y" in format_funcs:
|
|
36
|
-
format_funcs[y] = format_funcs["y"]
|
|
37
|
-
if z and "z" in format_funcs:
|
|
38
|
-
format_funcs[z] = format_funcs["z"]
|
|
39
|
-
return format_funcs
|
|
40
|
-
|
|
41
|
-
# endregion
|
|
42
|
-
|
|
43
|
-
# region Style
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
MAX_RESULTS = 50
|
|
47
|
-
X_COL = "index"
|
|
48
|
-
Y_COL = "overlap"
|
|
49
|
-
Z_COL = "users"
|
|
50
|
-
FIG_SIZE = (19.2, 10.8)
|
|
51
|
-
BACKGROUND_COLOR = 'black'
|
|
52
|
-
TEXT_COLOR = 'white'
|
|
53
|
-
PALETTE = "Greys_r"
|
|
54
|
-
FONT_SIZE = 14
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@dataclass
|
|
58
|
-
class StyleTemplate:
|
|
59
|
-
background_color: str = BACKGROUND_COLOR
|
|
60
|
-
fig_border: str = BACKGROUND_COLOR
|
|
61
|
-
font_name: str = 'Arial'
|
|
62
|
-
font_size: int = FONT_SIZE
|
|
63
|
-
font_color: str = TEXT_COLOR
|
|
64
|
-
palette: str = PALETTE
|
|
65
|
-
legend: bool = True
|
|
66
|
-
xscale: Optional[str] = None
|
|
67
|
-
x_ticks: int = 10
|
|
68
|
-
yscale: Optional[str] = None
|
|
69
|
-
y_ticks: int = 5
|
|
70
|
-
format_funcs: Optional[Dict[str, Optional[Callable[[
|
|
71
|
-
Union[int, float, str]], str]]]] = None
|
|
72
|
-
col_widths: Optional[List[float]] = None
|
|
73
|
-
|
|
74
|
-
@property
|
|
75
|
-
def font_mapping(self):
|
|
76
|
-
return {0: self.font_size-3,
|
|
77
|
-
1: self.font_size-1,
|
|
78
|
-
2: self.font_size,
|
|
79
|
-
3: self.font_size+1,
|
|
80
|
-
4: self.font_size+3}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
class DynamicFuncFormatter(FuncFormatter):
|
|
84
|
-
def __init__(self, func_name):
|
|
85
|
-
super().__init__(func_name)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def percent_formatter(val, pos: Optional[int] = None):
|
|
89
|
-
if val*100 <= 0.1: # For 0.1%
|
|
90
|
-
return f"{val*100:.2f}%"
|
|
91
|
-
elif val*100 <= 1: # For 1%
|
|
92
|
-
return f"{val*100:.1f}%"
|
|
93
|
-
else:
|
|
94
|
-
return f"{val*100:.0f}%"
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def bmk_formatter(val, pos: Optional[int] = None):
|
|
98
|
-
if val >= 1_000_000_000: # Billions
|
|
99
|
-
return f"{val / 1_000_000_000:.2f}B"
|
|
100
|
-
elif val >= 1_000_000: # Millions
|
|
101
|
-
return f"{val / 1_000_000:.1f}M"
|
|
102
|
-
elif val >= 1_000: # Thousands
|
|
103
|
-
return f"{val / 1_000:.1f}K"
|
|
104
|
-
else:
|
|
105
|
-
return f"{int(val)}"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def integer_formatter(value, pos: Optional[int] = None):
|
|
109
|
-
return f"{int(value)}"
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def string_formatter(value, pos: Optional[int] = None):
|
|
113
|
-
return str(value).replace("-", " ").replace("_", " ").title()
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def yy_mm__formatter(x, pos: Optional[int] = None):
|
|
117
|
-
return num2date(x).strftime('%Y-%m')
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def yy_mm_dd__formatter(x, pos: Optional[int] = None):
|
|
121
|
-
return num2date(x).strftime('%Y-%m-%D')
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def percent_formatter(x, pos: Optional[int] = None):
|
|
125
|
-
return f"{x * 100:.0f}%"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def generate_ticks(min_val, max_val, num_ticks="10"):
|
|
129
|
-
# Identify the type of the input
|
|
130
|
-
try:
|
|
131
|
-
min_val = float(min_val)
|
|
132
|
-
max_val = float(max_val)
|
|
133
|
-
is_date = False
|
|
134
|
-
except ValueError:
|
|
135
|
-
is_date = True
|
|
136
|
-
|
|
137
|
-
# Convert string inputs to appropriate numerical or date types
|
|
138
|
-
num_ticks = int(num_ticks)
|
|
139
|
-
|
|
140
|
-
if is_date:
|
|
141
|
-
min_val = pd.Timestamp(min_val).to_datetime64()
|
|
142
|
-
max_val = pd.Timestamp(max_val).to_datetime64()
|
|
143
|
-
data_range = (max_val - min_val).astype('timedelta64[D]').astype(int)
|
|
144
|
-
else:
|
|
145
|
-
data_range = max_val - min_val
|
|
146
|
-
|
|
147
|
-
# Calculate a nice step size
|
|
148
|
-
step_size = data_range / (num_ticks - 1)
|
|
149
|
-
|
|
150
|
-
# If date, convert back to datetime
|
|
151
|
-
if is_date:
|
|
152
|
-
ticks = pd.date_range(
|
|
153
|
-
start=min_val, periods=num_ticks, freq=f"{step_size}D")
|
|
154
|
-
else:
|
|
155
|
-
# Round the step size to a "nice" number
|
|
156
|
-
exponent = np.floor(np.log10(step_size))
|
|
157
|
-
fraction = step_size / 10**exponent
|
|
158
|
-
nice_fraction = round(fraction)
|
|
159
|
-
|
|
160
|
-
# Create nice step size
|
|
161
|
-
nice_step = nice_fraction * 10**exponent
|
|
162
|
-
|
|
163
|
-
# Generate the tick marks based on the nice step size
|
|
164
|
-
ticks = np.arange(min_val, max_val + nice_step, nice_step)
|
|
165
|
-
|
|
166
|
-
return ticks
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
# endregion
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|