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