MatplotLibAPI 2.0.2__tar.gz → 3.0.1__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.
Files changed (26) hide show
  1. matplotlibapi-3.0.1/MatplotLibAPI/Bubble.py +127 -0
  2. matplotlibapi-3.0.1/MatplotLibAPI/Composite.py +78 -0
  3. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/MatplotLibAPI/Network.py +61 -32
  4. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/MatplotLibAPI/Pivot.py +84 -94
  5. matplotlibapi-3.0.1/MatplotLibAPI/Style.py +171 -0
  6. matplotlibapi-3.0.1/MatplotLibAPI/Table.py +65 -0
  7. matplotlibapi-3.0.1/MatplotLibAPI/Timeserie.py +97 -0
  8. matplotlibapi-3.0.1/MatplotLibAPI/__init__.py +13 -0
  9. matplotlibapi-3.0.1/MatplotLibAPI/pdAccessor.py +125 -0
  10. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1/MatplotLibAPI.egg-info}/PKG-INFO +1 -1
  11. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/MatplotLibAPI.egg-info/SOURCES.txt +3 -2
  12. {MatplotLibAPI-2.0.2/MatplotLibAPI.egg-info → matplotlibapi-3.0.1}/PKG-INFO +1 -1
  13. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/setup.py +1 -1
  14. MatplotLibAPI-2.0.2/MatplotLibAPI/Bubble.py +0 -108
  15. MatplotLibAPI-2.0.2/MatplotLibAPI/Composite.py +0 -109
  16. MatplotLibAPI-2.0.2/MatplotLibAPI/Table.py +0 -70
  17. MatplotLibAPI-2.0.2/MatplotLibAPI/TimeSeries.py +0 -136
  18. MatplotLibAPI-2.0.2/MatplotLibAPI/Utils.py +0 -204
  19. MatplotLibAPI-2.0.2/MatplotLibAPI/__init__.py +0 -0
  20. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/LICENSE +0 -0
  21. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/MatplotLibAPI.egg-info/dependency_links.txt +0 -0
  22. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/MatplotLibAPI.egg-info/requires.txt +0 -0
  23. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/MatplotLibAPI.egg-info/top_level.txt +0 -0
  24. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/README.md +0 -0
  25. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/pyproject.toml +0 -0
  26. {MatplotLibAPI-2.0.2 → matplotlibapi-3.0.1}/setup.cfg +0 -0
@@ -0,0 +1,127 @@
1
+ # Hint for Visual Code Python Interactive window
2
+ # %%
3
+
4
+ import pandas as pd
5
+ import matplotlib.pyplot as plt
6
+ from matplotlib.axes import Axes
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
+
11
+
12
+ BUBBLE_STYLE_TEMPLATE = StyleTemplate(
13
+ format_funcs={"label": string_formatter,
14
+ "x": bmk_formatter,
15
+ "y": percent_formatter,
16
+ "label": string_formatter,
17
+ "z": bmk_formatter},
18
+ yscale="log",
19
+ y_ticks=8,
20
+ x_ticks=8
21
+ )
22
+
23
+
24
+ def plot_bubble(
25
+ pd_df: pd.DataFrame,
26
+ label: str,
27
+ x: str,
28
+ y: str,
29
+ z: str,
30
+ title: Optional[str] = "Test",
31
+ style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
32
+ max_values: int = BUBBLE_STYLE_TEMPLATE,
33
+ center_to_mean: bool = False,
34
+ sort_by: Optional[str] = None,
35
+ ascending: bool = False,
36
+ ax: Optional[Axes] = None):
37
+
38
+ _validate_panda(pd_df, cols=[label, x, y, z], sort_by=sort_by)
39
+ style.format_funcs = format_func(
40
+ style.format_funcs, label=label, x=x, y=y, z=z)
41
+ if not sort_by:
42
+ sort_by = z
43
+
44
+ plot_df = pd_df[[label, x, y, z]].sort_values(
45
+ by=sort_by, ascending=ascending).head(max_values)
46
+ if center_to_mean:
47
+ x_col_mean = plot_df[x].mean()
48
+ plot_df[x] = plot_df[x] - x_col_mean
49
+ plot_df['quintile'] = pd.qcut(
50
+ plot_df[z], 5, labels=False)
51
+
52
+ # styling
53
+
54
+ plot_df["fontsize"] = plot_df['quintile'].map(style.font_mapping)
55
+
56
+ if not ax:
57
+ ax = plt.gca()
58
+
59
+ ax = sns.scatterplot(
60
+ data=plot_df,
61
+ x=x,
62
+ y=y,
63
+ size=z,
64
+ hue='quintile',
65
+ sizes=(100, 2000),
66
+ legend=False,
67
+ palette=sns.color_palette(style.palette, as_cmap=True),
68
+ edgecolor=style.background_color,
69
+ ax=ax)
70
+ ax.set_facecolor(style.background_color)
71
+ if style.xscale:
72
+ ax.set(xscale=style.xscale)
73
+ if style.yscale:
74
+ ax.set(yscale=style.yscale)
75
+
76
+ x_min = pd_df[x].min()
77
+ x_max = pd_df[x].max()
78
+ x_mean = pd_df[x].mean()
79
+ ax.set_xticks(generate_ticks(x_min, x_max, num_ticks=style.x_ticks))
80
+ ax.xaxis.grid(True, "major", linewidth=.5, color=style.font_color)
81
+ if style.format_funcs.get("x"):
82
+ ax.xaxis.set_major_formatter(
83
+ DynamicFuncFormatter(style.format_funcs.get("x")))
84
+
85
+ y_min = pd_df[y].min()
86
+ y_max = pd_df[y].max()
87
+ y_mean = pd_df[y].mean()
88
+ ax.set_yticks(generate_ticks(y_min, y_max, num_ticks=style.y_ticks))
89
+ ax.yaxis.grid(True, "major", linewidth=.5, color=style.font_color)
90
+ if style.format_funcs.get("y"):
91
+ ax.yaxis.set_major_formatter(
92
+ DynamicFuncFormatter(style.format_funcs.get("y")))
93
+
94
+ ax.tick_params(axis='both',
95
+ which='major',
96
+ colors=style.font_color,
97
+ labelsize=style.font_size)
98
+
99
+ ax.vlines(x=x_mean,
100
+ ymin=y_min,
101
+ ymax=y_max,
102
+ linestyle='--',
103
+ colors=style.font_color)
104
+ ax.hlines(y=y_mean,
105
+ xmin=x_min,
106
+ xmax=x_max,
107
+ linestyle='--',
108
+ colors=style.font_color)
109
+
110
+ for index, row in plot_df.iterrows():
111
+ x_value = row[x]
112
+ y_value = row[y]
113
+ s_value = str(row[label])
114
+ if style.format_funcs.get("label"):
115
+ s_value = style.format_funcs.get("label")(s_value)
116
+ fs = row["fontsize"]
117
+ ax.text(x_value,
118
+ y_value,
119
+ s_value,
120
+ horizontalalignment='center',
121
+ fontdict={'color': style.font_color, 'fontsize': fs})
122
+ if title:
123
+ ax.set_title(title, color=style.font_color, fontsize=style.font_size*2)
124
+ return ax
125
+
126
+
127
+ # endregion
@@ -0,0 +1,78 @@
1
+ # Hint for Visual Code Python Interactive window
2
+ # %%
3
+ import matplotlib.pyplot as plt
4
+ from matplotlib.figure import Figure
5
+ import pandas as pd
6
+ from .Bubble import plot_bubble, BUBBLE_STYLE_TEMPLATE
7
+ from .Table import plot_table
8
+ from typing import Optional, Tuple
9
+ from .Style import StyleTemplate, _validate_panda,format_func
10
+
11
+
12
+ def plot_composite_bubble(
13
+ pd_df: pd.DataFrame,
14
+ label: str,
15
+ x: str,
16
+ y: str,
17
+ z: str,
18
+ title: Optional[str] = "Test",
19
+ style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
20
+ max_values: int = 50,
21
+ center_to_mean: bool = False,
22
+ sort_by: Optional[str] = None,
23
+ ascending: bool = False,
24
+ table_rows: int = 10,
25
+ figsize: Tuple[float, float] = (19.2, 10.8)) -> Figure:
26
+
27
+ _validate_panda(pd_df, cols=[label, x, y, z], sort_by=sort_by)
28
+ style.format_funcs=format_func(style.format_funcs,label=label,x=x,y=y)
29
+ fig = plt.figure(figsize=figsize)
30
+ fig.patch.set_facecolor("black")
31
+ grid = plt.GridSpec(2, 2, height_ratios=[2, 1], width_ratios=[1, 1])
32
+ ax = fig.add_subplot(grid[0, 0:])
33
+ ax = plot_bubble(pd_df=pd_df,
34
+ label=label,
35
+ x=x,
36
+ y=y,
37
+ z=z,
38
+ title=title,
39
+ style=style,
40
+ max_values=max_values,
41
+ center_to_mean=center_to_mean,
42
+ sort_by=sort_by,
43
+ ascending=ascending,
44
+ ax=ax)
45
+
46
+ if "label" in style.format_funcs:
47
+ style.format_funcs[label] = style.format_funcs["label"]
48
+ if "x" in style.format_funcs:
49
+ style.format_funcs[x] = style.format_funcs["x"]
50
+ if "y" in style.format_funcs:
51
+ style.format_funcs[y] = style.format_funcs["y"]
52
+ if "z" in style.format_funcs:
53
+ style.format_funcs[z] = style.format_funcs["z"]
54
+
55
+ ax2 = fig.add_subplot(grid[1, 0])
56
+ ax2 = plot_table(
57
+ pd_df=pd_df,
58
+ cols=[label, z, y, x],
59
+ title=f"Top {table_rows}",
60
+ ax=ax2,
61
+ sort_by=sort_by,
62
+ ascending=False,
63
+ max_values=table_rows,
64
+ style=style
65
+ )
66
+ ax3 = fig.add_subplot(grid[1, 1])
67
+ ax3 = plot_table(
68
+ pd_df=pd_df,
69
+ cols=[label, z, y, x],
70
+ title=f"Worst {table_rows}",
71
+ ax=ax3,
72
+ sort_by=sort_by,
73
+ ascending=True,
74
+ max_values=table_rows,
75
+ style=style
76
+ )
77
+ fig.tight_layout()
78
+ return fig
@@ -4,19 +4,27 @@ from collections.abc import Iterable
4
4
  from typing import Any, Dict, List, Optional, Tuple
5
5
 
6
6
  import matplotlib.pyplot as plt
7
+ from matplotlib.axes import Axes
8
+ import seaborn as sns
7
9
  import networkx as nx
8
10
  import numpy as np
9
11
  import pandas as pd
10
12
  from networkx import Graph
11
13
  from networkx.classes.graph import Graph
12
14
 
15
+
16
+ from .Style import StyleTemplate, string_formatter, _validate_panda,format_func
17
+
18
+ NETWORK_STYLE_TEMPLATE = StyleTemplate(
19
+ )
20
+
13
21
  DEFAULT = {"MAX_EDGES": 100,
14
22
  "MAX_NODES": 30,
15
23
  "MIN_NODE_SIZE": 100,
16
24
  "MAX_NODE_SIZE": 2000,
17
25
  "MAX_EDGE_WIDTH": 10,
18
26
  "GRAPH_SCALE": 2,
19
- "MAX_FONT_SIZE": 12,
27
+ "MAX_FONT_SIZE": 20,
20
28
  "MIN_FONT_SIZE": 8
21
29
  }
22
30
 
@@ -25,7 +33,7 @@ def softmax(x):
25
33
  return (np.exp(x - np.max(x)) / np.exp(x - np.max(x)).sum())
26
34
 
27
35
 
28
- def scale_weights(weights, scale_min=0,scale_max=1):
36
+ def scale_weights(weights, scale_min=0, scale_max=1):
29
37
  deciles = np.percentile(weights, [10, 20, 30, 40, 50, 60, 70, 80, 90])
30
38
  outs = np.searchsorted(deciles, weights)
31
39
  return [out * (scale_max-scale_min)/len(deciles)+scale_min for out in outs]
@@ -160,8 +168,9 @@ class Graph(nx.Graph):
160
168
  def subgraphX(self, node_list=None, max_edges: int = DEFAULT["MAX_EDGES"]):
161
169
  if node_list is None:
162
170
  node_list = self.nodes.sort("weight")[:DEFAULT["MAX_NODES"]]
163
- connected_subgraph_nodes=list(self.find_connected_subgraph())
164
- node_list = [node for node in node_list if node in connected_subgraph_nodes]
171
+ connected_subgraph_nodes = list(self.find_connected_subgraph())
172
+ node_list = [
173
+ node for node in node_list if node in connected_subgraph_nodes]
165
174
 
166
175
  subgraph = nx.subgraph(
167
176
  self, nbunch=node_list)
@@ -169,30 +178,39 @@ class Graph(nx.Graph):
169
178
  subgraph = subgraph.edge_subgraph(list(edges)[:max_edges])
170
179
  return subgraph
171
180
 
172
- def plotX(self):
181
+ def plotX(self,
182
+ title: str = "Test",
183
+ style: StyleTemplate = NETWORK_STYLE_TEMPLATE,
184
+ ax: Optional[Axes] = None) -> Axes:
173
185
  """
174
186
  Plots the degree distribution of the graph, including a degree rank plot and a degree histogram.
175
187
  """
176
188
  degree_sequence = sorted([d for n, d in self.degree()], reverse=True)
177
189
  dmax = max(degree_sequence)
178
-
179
- fig, ax = plt.subplots()
190
+ sns.set_palette(style.palette)
191
+ if ax is None:
192
+ ax = plt.gca()
180
193
 
181
194
  node_sizes, edge_widths, font_sizes = self.layout(
182
- DEFAULT["MAX_NODE_SIZE"], DEFAULT["MAX_EDGE_WIDTH"], 14)
195
+ min_node_size=DEFAULT["MIN_NODE_SIZE"]/5,
196
+ max_node_size=DEFAULT["MAX_NODE_SIZE"],
197
+ max_edge_width=DEFAULT["MAX_EDGE_WIDTH"],
198
+ min_font_size=style.font_mapping.get(0),
199
+ max_font_size=style.font_mapping.get(4))
183
200
  pos = nx.spring_layout(self, k=1)
184
201
  # nodes
185
202
  nx.draw_networkx_nodes(self,
186
203
  pos,
187
204
  ax=ax,
188
205
  node_size=list(node_sizes),
189
- # node_color=list(node_sizes.values()),
190
- cmap=plt.cm.Blues)
206
+ node_color=node_sizes,
207
+ cmap=plt.cm.get_cmap(style.palette))
191
208
  # edges
192
209
  nx.draw_networkx_edges(self,
193
210
  pos,
194
211
  ax=ax,
195
- alpha=0.4,
212
+ edge_color=style.font_color,
213
+ edge_cmap=plt.cm.get_cmap(style.palette),
196
214
  width=edge_widths)
197
215
  # labels
198
216
  for font_size, nodes in font_sizes.items():
@@ -201,16 +219,13 @@ class Graph(nx.Graph):
201
219
  pos,
202
220
  ax=ax,
203
221
  font_size=font_size,
204
- labels={n: n for n in nodes},
205
- alpha=0.4)
206
-
207
- ax.set_title(self.name)
222
+ font_color=style.font_color,
223
+ labels={n: string_formatter(n) for n in nodes})
224
+ ax.set_facecolor(style.background_color)
225
+ ax.set_title(title, color=style.font_color, fontsize=style.font_size*2)
208
226
  ax.set_axis_off()
209
227
 
210
-
211
-
212
- fig.tight_layout()
213
- return fig
228
+ return ax
214
229
 
215
230
  def analysis(self, node_list: Optional[List] = None,
216
231
  scale: int = DEFAULT["GRAPH_SCALE"],
@@ -242,12 +257,14 @@ class Graph(nx.Graph):
242
257
  for node in list(H.nodes):
243
258
  if H.degree(node) < 2:
244
259
  # Remove the node and its incident edges
245
- logging.info(f'Removing the {node} node and its incident edges')
260
+ logging.info(
261
+ f'Removing the {node} node and its incident edges')
246
262
  H.remove_node(node)
247
263
  removed_node = True
248
264
  break
249
265
 
250
266
  return H
267
+
251
268
  def top_k_edges(self, attribute: str, reverse: bool = True, k: int = 5) -> Dict[Any, List[Tuple[Any, Dict]]]:
252
269
  """
253
270
  Returns the top k edges per node based on the given attribute.
@@ -275,10 +292,10 @@ class Graph(nx.Graph):
275
292
  return top_list
276
293
 
277
294
  @staticmethod
278
- def from_pandas_edgelist(df,
279
- source: Optional[str] = "source",
280
- target: Optional[str] = "target",
281
- weight: Optional[str] = "weight"):
295
+ def from_pandas_edgelist(df: pd.DataFrame,
296
+ source: str = "source",
297
+ target: str = "target",
298
+ weight: str = "weight"):
282
299
  """
283
300
  Initialize netX instance with a simple dataframe
284
301
 
@@ -291,7 +308,7 @@ class Graph(nx.Graph):
291
308
  G = Graph()
292
309
  G = nx.from_pandas_edgelist(
293
310
  df, source=source, target=target, edge_attr=weight, create_using=G)
294
- G=G.find_connected_subgraph()
311
+ G = G.find_connected_subgraph()
295
312
 
296
313
  edge_aggregates = G.top_k_edges(attribute=weight, k=10)
297
314
  node_aggregates = {}
@@ -308,11 +325,23 @@ class Graph(nx.Graph):
308
325
  G = G.edge_subgraph(edges=G.top_k_edges(attribute=weight))
309
326
  return G
310
327
 
311
- def plot_network(data:pd.DataFrame):
312
- graph = Graph.from_pandas_edgelist(data)
313
- graph = graph.subgraphX()
314
- return graph.analysis()
315
-
316
-
317
-
318
328
 
329
+ def plot_network(pd_df: pd.DataFrame,
330
+ source: str = "source",
331
+ target: str = "target",
332
+ weight: str = "weight",
333
+ title: str = "Test",
334
+ style: StyleTemplate = NETWORK_STYLE_TEMPLATE,
335
+ sort_by: Optional[str] = None,
336
+ ascending: bool = False,
337
+ ax: Optional[Axes] = None) -> Axes:
338
+
339
+ _validate_panda(pd_df, cols=[source, target, weight], sort_by=sort_by)
340
+
341
+ graph = Graph.from_pandas_edgelist(pd_df,
342
+ source=source,
343
+ target=target,
344
+ weight=weight)
345
+ return graph.plotX(title=title,
346
+ style=style,
347
+ ax=ax)
@@ -1,3 +1,5 @@
1
+ # Hint for Visual Code Python Interactive window
2
+ # %%
1
3
  from typing import List, Optional, Union
2
4
 
3
5
  import pandas as pd
@@ -7,113 +9,101 @@ import matplotlib.pyplot as plt
7
9
  from matplotlib.axes import Axes
8
10
  from matplotlib.dates import DateFormatter, MonthLocator
9
11
 
10
- from .Utils import (PIVOTBARS_STYLE_TEMPLATE, PIVOTLINES_STYLE_TEMPLATE,
11
- DynamicFuncFormatter, StyleTemplate, generate_ticks)
12
12
 
13
13
 
14
- def plot_pivotbar(data, metric, n_top, title):
15
- # Sort the data by metric column in descending order
16
- data_sorted = data.sort_values(by=metric, ascending=False)
17
-
18
- # Select the top rows
19
- top_rows = data_sorted.head(n_top)
20
-
21
- # Plotting the top 50 data points with tag labels
22
- fig, ax = plt.subplots(figsize=(12, 6))
23
-
24
- # Plot the 'Used' data points (where Used == 1) in green
25
- used_data = top_rows[top_rows['used'] == 1]
26
- ax.bar(used_data.tag, used_data[metric],
27
- color='green', label='Used', alpha=0.7)
28
-
29
- # Plot the 'Not Used' data points (where Used == 0) in red
30
- not_used_data = top_rows[top_rows['used'] == 0]
31
- ax.bar(not_used_data.tag, not_used_data[metric],
32
- color='red', label='Not Used', alpha=0.7)
14
+ from .Style import DynamicFuncFormatter, StyleTemplate, generate_ticks, string_formatter, _validate_panda, percent_formatter,format_func
15
+
16
+ PIVOTBARS_STYLE_TEMPLATE = StyleTemplate(
17
+ background_color='black',
18
+ fig_border='darkgrey',
19
+ font_color='white',
20
+ palette='magma',
21
+ format_funcs={"y": percent_formatter,
22
+ "label": string_formatter}
23
+ )
24
+ PIVOTLINES_STYLE_TEMPLATE = StyleTemplate(
25
+ background_color='white',
26
+ fig_border='lightgrey',
27
+ palette='viridis',
28
+ format_funcs={"y": percent_formatter, "label": string_formatter}
29
+ )
30
+
31
+ def plot_pivotbar(pd_df: pd.DataFrame,
32
+ label: str,
33
+ x: str,
34
+ y: str,
35
+ agg: str = "sum",
36
+ style: StyleTemplate = PIVOTBARS_STYLE_TEMPLATE,
37
+ title: Optional[str] = None,
38
+ sort_by: Optional[str] = None,
39
+ ascending: bool = False,
40
+ ax: Optional[Axes] = None):
41
+
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)
44
+ pivot_df = pd.pivot_table(pd_df, values=y, index=[
45
+ x], columns=[label], aggfunc=agg)
46
+ # Reset index to make x a column again
47
+ pivot_df = pivot_df.reset_index()
48
+
49
+ if not ax:
50
+ ax = plt.gca()
51
+
52
+ # Plot each label's data
53
+ for column in pivot_df.columns[1:]:
54
+ _label = column
55
+ if style.format_funcs.get(column):
56
+ _label = style.format_funcs[column](column)
57
+ ax.bar(x=pivot_df[x],
58
+ height=pivot_df[column],
59
+ label=_label, alpha=0.7)
33
60
 
34
61
  # Set labels and title
35
- ax.set_ylabel('UVs')
36
- ax.set_title(f'{title}\nTop {n_top} tags')
37
- ax.legend()
62
+ ax.set_ylabel(string_formatter(y))
63
+ ax.set_xlabel(string_formatter(x))
64
+ if title:
65
+ ax.set_title(f'{title}')
66
+ ax.legend(fontsize=style.font_size-2,
67
+ title_fontsize=style.font_size+2,
68
+ labelcolor='linecolor',
69
+ facecolor=style.background_color)
38
70
 
39
71
  ax.tick_params(axis='x', rotation=90)
40
- return fig
41
-
42
-
43
- def plot_lines(ax: Axes,
44
- data: pd.DataFrame,
45
- x_col: str,
46
- y_col: Union[str, List[str]],
47
- style: Optional[StyleTemplate] = None,
48
- fig_title: Optional[str] = None,
49
- n_top: int = 4,
50
- z_col: str = "browser") -> Axes:
51
- """
52
- This function plots time series lines for the top n elements in the specified dimension.
53
-
54
- Parameters:
55
- ax (matplotlib.axes._axes.Axes): The ax to plot on.
56
- data (pd.DataFrame): The data to plot.
57
- metrics (Union[str, List[str]]): The column name(s) in data to plot.
58
- date_col (str): The column name containing the date information.
59
- ... (other parameters): Various parameters to customize the plot.
60
- date_format (str): The format of the date to display on the x-axis.
61
- date_locator (matplotlib.dates.Locator): Locator object to determine the date ticks on the x-axis.
62
-
63
- Returns:
64
- ax (matplotlib.axes._axes.Axes): The ax with the plot.
65
- """
72
+ return ax
66
73
 
67
- # Validate inputs
68
- if x_col not in data.columns:
69
- raise ValueError(f"'{x_col}' column not found in the data")
70
- if not isinstance(y_col, list) and not isinstance(y_col, str):
71
- raise TypeError("'metrics' should be a string or a list of strings")
72
- if isinstance(y_col, list) and not len(y_col) >= 2:
73
- raise ValueError(
74
- f"metrics should be 2 of lengths column not found in the data")
75
- ax.clear()
76
- if fig_title is not None:
77
- ax.set_title(fig_title)
78
- if style is None:
79
- style = PIVOTLINES_STYLE_TEMPLATE
80
- ax.figure.set_facecolor(style.fig_background_color)
81
- ax.figure.set_edgecolor(style.fig_border)
82
74
 
83
- display_metric = y_col[0]
84
- sort_metric = y_col[1]
75
+ def plot_lines(
76
+ data: pd.DataFrame,
77
+ label: str,
78
+ x: str,
79
+ y: Union[str, List[str]],
80
+ title: Optional[str] = None,
81
+ style: Optional[StyleTemplate] = PIVOTBARS_STYLE_TEMPLATE,
82
+ max_values: int = 4,
83
+ sort_by: Optional[str] = None,
84
+ ascending: bool = False,
85
+ ax: Optional[Axes] = None
86
+ ) -> Axes:
87
+
88
+ if title is not None:
89
+ ax.set_title(title)
90
+ ax.figure.set_facecolor(style.background_color)
91
+ ax.figure.set_edgecolor(style.fig_border)
85
92
  # Get the top n elements in the specified z
86
93
  top_elements = data.groupby(
87
- z_col)[sort_metric].sum().nlargest(n_top).index.tolist()
88
- top_elements_df = data[data[z_col].isin(top_elements)]
94
+ label)[y].sum().nlargest(max_values).index.tolist()
95
+ top_elements_df = data[data[label].isin(top_elements)]
89
96
  y_min = 0
90
97
  # Plot the time series lines for each of the top elements
91
98
  for element in top_elements:
92
- subset = top_elements_df[top_elements_df[z_col] == element]
93
- # Define the line style based on the element name
94
- if element == "Chrome":
95
- line_style = '-'
96
- color = 'green'
97
- elif element == "Android Webview":
98
- line_style = '--'
99
- color = 'green'
100
- elif element == "Safari":
101
- line_style = '-'
102
- color = 'red'
103
- elif element == "Safari (in-app)":
104
- line_style = '--'
105
- color = 'red'
106
- else:
107
- line_style = '-'
108
- color = 'black'
109
- y_min = min(y_min, subset[display_metric].min())
110
-
111
- ax.plot(subset[x_col], subset[display_metric], label=element)
99
+ subset = top_elements_df[top_elements_df[label] == element]
100
+ y_min = min(y_min, subset[y].min())
101
+ ax.plot(subset[x], subset[y], label=element)
112
102
 
113
103
  # Set x-axis date format and locator
114
104
  if style.x_formatter is not None:
115
- x_min = data[x_col].min()
116
- x_max = data[x_col].max()
105
+ x_min = data[x].min()
106
+ x_max = data[x].max()
117
107
 
118
108
  if style.x_formatter == "year_month_formatter":
119
109
  ax.xaxis.set_major_locator(plt.matplotlib.dates.MonthLocator())
@@ -126,11 +116,11 @@ def plot_lines(ax: Axes,
126
116
  ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
127
117
 
128
118
  # Set title and labels
129
- ax.set_xlabel(x_col)
130
- y_max = data[display_metric].dropna().quantile(0.95)
119
+ ax.set_xlabel(x)
120
+ y_max = data[y].dropna().quantile(0.95)
131
121
 
132
122
  ax.set_ylim(y_min, y_max)
133
- ax.set_ylabel(display_metric)
123
+ ax.set_ylabel(y)
134
124
  if style.y_formatter is not None:
135
125
  ax.yaxis.set_major_formatter(
136
126
  DynamicFuncFormatter(style.y_formatter))