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 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
- _validate_panda(pd_df, cols=[label, x, y, z], sort_by=sort_by)
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:
@@ -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
- import pandas as pd
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 typing import Optional, Tuple
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
- _validate_panda(pd_df, cols=[label, x, y, z], sort_by=sort_by)
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 .Style import StyleTemplate, string_formatter, _validate_panda,format_func
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 subgraphX(self, node_list=None, max_edges: int = DEFAULT["MAX_EDGES"]):
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 plotX(self,
182
- title: str = "Test",
183
- style: StyleTemplate = NETWORK_STYLE_TEMPLATE,
184
- ax: Optional[Axes] = None) -> Axes:
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 analysis(self, node_list: Optional[List] = None,
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.subgraphX(max_edges=max_edges, node_list=node_list)
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.subgraphX(max_edges=max_edges,
243
- node_list=connected_component)
244
- connected_component_graph.plotX()
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
- _validate_panda(pd_df, cols=[source, target, weight], sort_by=sort_by)
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.plotX(title=title,
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 .Style import DynamicFuncFormatter, StyleTemplate, generate_ticks, string_formatter, _validate_panda, percent_formatter,format_func
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
- _validate_panda(pd_df, cols=[label, x, y], sort_by=sort_by)
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
- from .Style import StyleTemplate, _validate_panda, string_formatter
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
- _validate_panda(pd_df, cols=cols, sort_by=sort_by)
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]
@@ -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
- from .Style import DynamicFuncFormatter, StyleTemplate, string_formatter, _validate_panda, bmk_formatter, format_func
9
- from typing import Optional
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
- _validate_panda(pd_df, cols=[label, x, y], sort_by=sort_by)
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
- from typing import List, Optional
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
- from .Style import DynamicFuncFormatter, StyleTemplate, generate_ticks, string_formatter, _validate_panda, percent_formatter, format_func
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
- _validate_panda(df, cols=cols, sort_by=sort_by)
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
- df['color'] = df[color]
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(data=data)
63
+ fig = go.Figure(g)
45
64
  else:
46
- fig.add_trace(go.Treemap(data))
47
-
65
+ fig.add_trace(g)
66
+
48
67
  fig.update_layout(
49
- paper_bgcolor=style.background_color,
68
+ title=title,
50
69
  plot_bgcolor=style.background_color,
51
- font=dict(color=style.font_color),
52
- margin=dict(t=50, l=25, r=25, b=25))
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 .Table import plot_table
4
- from .Timeserie import plot_timeserie
5
- from .Bubble import plot_bubble
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 .pdAccessor import MatPlotLibAccessor
11
- from .Style import StyleTemplate
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", "MatPlotLibAccessor"]
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.0
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 Wrapper
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
@@ -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,,