py2ls 0.1.8.6__py3-none-any.whl → 0.1.8.8__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.
py2ls/plot.py CHANGED
@@ -4,6 +4,9 @@ import pandas as pd
4
4
  from matplotlib.colors import to_rgba
5
5
  from scipy.stats import gaussian_kde
6
6
 
7
+ import matplotlib
8
+ import matplotlib.ticker as tck
9
+ from cycler import cycler
7
10
  import logging
8
11
 
9
12
  # Suppress INFO messages from fontTools
@@ -18,25 +21,38 @@ def catplot(data, *args, **kwargs):
18
21
  data (array): data matrix
19
22
  """
20
23
 
21
- def plot_bars(data, data_m, opt_b, xloc, ax):
24
+ def plot_bars(data, data_m, opt_b, xloc, ax, label=None):
22
25
  bar_positions = get_positions(
23
26
  xloc, opt_b["loc"], opt_b["x_width"], data.shape[0]
24
27
  )
25
28
  bar_positions = np.nanmean(bar_positions, axis=0)
26
29
  for i, (x, y) in enumerate(zip(bar_positions, data_m)):
27
30
  color = to_rgba(opt_b["FaceColor"][i % len(opt_b["FaceColor"])])
28
- ax.bar(
29
- x,
30
- y,
31
- width=opt_b["x_width"],
32
- color=color,
33
- edgecolor=opt_b["EdgeColor"],
34
- alpha=opt_b["FaceAlpha"],
35
- linewidth=opt_b["LineWidth"],
36
- hatch=opt_b["hatch"],
37
- )
31
+ if label is not None and i < len(label):
32
+ ax.bar(
33
+ x,
34
+ y,
35
+ width=opt_b["x_width"],
36
+ color=color,
37
+ edgecolor=opt_b["EdgeColor"],
38
+ alpha=opt_b["FaceAlpha"],
39
+ linewidth=opt_b["LineWidth"],
40
+ hatch=opt_b["hatch"],
41
+ label=label[i],
42
+ )
43
+ else:
44
+ ax.bar(
45
+ x,
46
+ y,
47
+ width=opt_b["x_width"],
48
+ color=color,
49
+ edgecolor=opt_b["EdgeColor"],
50
+ alpha=opt_b["FaceAlpha"],
51
+ linewidth=opt_b["LineWidth"],
52
+ hatch=opt_b["hatch"],
53
+ )
38
54
 
39
- def plot_errors(data, data_m, opt_e, xloc, ax):
55
+ def plot_errors(data, data_m, opt_e, xloc, ax, label=None):
40
56
  error_positions = get_positions(
41
57
  xloc, opt_e["loc"], opt_e["x_width"], data.shape[0]
42
58
  )
@@ -50,59 +66,110 @@ def catplot(data, *args, **kwargs):
50
66
  if not isinstance(opt_e["MarkerEdgeColor"], list):
51
67
  opt_e["MarkerEdgeColor"] = [opt_e["MarkerEdgeColor"]]
52
68
  for i, (x, y, err) in enumerate(zip(error_positions, data_m, errors)):
53
- if opt_e["MarkerSize"] == "auto":
54
- ax.errorbar(
55
- x,
56
- y,
57
- yerr=err,
58
- fmt=opt_e["Marker"],
59
- ecolor=opt_e["LineColor"],
60
- elinewidth=opt_e["LineWidth"],
61
- lw=opt_e["LineWidth"],
62
- ls=opt_e["LineStyle"],
63
- capsize=opt_e["CapSize"],
64
- capthick=opt_e["CapLineWidth"],
65
- mec=opt_e["MarkerEdgeColor"][i % len(opt_e["MarkerEdgeColor"])],
66
- mfc=opt_e["FaceColor"][i % len(opt_e["FaceColor"])],
67
- visible=opt_e["Visible"],
68
- )
69
+ if label is not None and i < len(label):
70
+ if opt_e["MarkerSize"] == "auto":
71
+ ax.errorbar(
72
+ x,
73
+ y,
74
+ yerr=err,
75
+ fmt=opt_e["Marker"],
76
+ ecolor=opt_e["LineColor"],
77
+ elinewidth=opt_e["LineWidth"],
78
+ lw=opt_e["LineWidth"],
79
+ ls=opt_e["LineStyle"],
80
+ capsize=opt_e["CapSize"],
81
+ capthick=opt_e["CapLineWidth"],
82
+ mec=opt_e["MarkerEdgeColor"][i % len(opt_e["MarkerEdgeColor"])],
83
+ mfc=opt_e["FaceColor"][i % len(opt_e["FaceColor"])],
84
+ visible=opt_e["Visible"],
85
+ label=label[i],
86
+ )
87
+ else:
88
+ ax.errorbar(
89
+ x,
90
+ y,
91
+ yerr=err,
92
+ fmt=opt_e["Marker"],
93
+ ecolor=opt_e["LineColor"],
94
+ elinewidth=opt_e["LineWidth"],
95
+ lw=opt_e["LineWidth"],
96
+ ls=opt_e["LineStyle"],
97
+ capsize=opt_e["CapSize"],
98
+ capthick=opt_e["CapLineWidth"],
99
+ markersize=opt_e["MarkerSize"],
100
+ mec=opt_e["MarkerEdgeColor"][i % len(opt_e["MarkerEdgeColor"])],
101
+ mfc=opt_e["FaceColor"][i % len(opt_e["FaceColor"])],
102
+ visible=opt_e["Visible"],
103
+ label=label[i],
104
+ )
69
105
  else:
70
- ax.errorbar(
71
- x,
72
- y,
73
- yerr=err,
74
- fmt=opt_e["Marker"],
75
- ecolor=opt_e["LineColor"],
76
- elinewidth=opt_e["LineWidth"],
77
- lw=opt_e["LineWidth"],
78
- ls=opt_e["LineStyle"],
79
- capsize=opt_e["CapSize"],
80
- capthick=opt_e["CapLineWidth"],
81
- markersize=opt_e["MarkerSize"],
82
- mec=opt_e["MarkerEdgeColor"][i % len(opt_e["MarkerEdgeColor"])],
83
- mfc=opt_e["FaceColor"][i % len(opt_e["FaceColor"])],
84
- visible=opt_e["Visible"],
85
- )
106
+ if opt_e["MarkerSize"] == "auto":
107
+ ax.errorbar(
108
+ x,
109
+ y,
110
+ yerr=err,
111
+ fmt=opt_e["Marker"],
112
+ ecolor=opt_e["LineColor"],
113
+ elinewidth=opt_e["LineWidth"],
114
+ lw=opt_e["LineWidth"],
115
+ ls=opt_e["LineStyle"],
116
+ capsize=opt_e["CapSize"],
117
+ capthick=opt_e["CapLineWidth"],
118
+ mec=opt_e["MarkerEdgeColor"][i % len(opt_e["MarkerEdgeColor"])],
119
+ mfc=opt_e["FaceColor"][i % len(opt_e["FaceColor"])],
120
+ visible=opt_e["Visible"],
121
+ )
122
+ else:
123
+ ax.errorbar(
124
+ x,
125
+ y,
126
+ yerr=err,
127
+ fmt=opt_e["Marker"],
128
+ ecolor=opt_e["LineColor"],
129
+ elinewidth=opt_e["LineWidth"],
130
+ lw=opt_e["LineWidth"],
131
+ ls=opt_e["LineStyle"],
132
+ capsize=opt_e["CapSize"],
133
+ capthick=opt_e["CapLineWidth"],
134
+ markersize=opt_e["MarkerSize"],
135
+ mec=opt_e["MarkerEdgeColor"][i % len(opt_e["MarkerEdgeColor"])],
136
+ mfc=opt_e["FaceColor"][i % len(opt_e["FaceColor"])],
137
+ visible=opt_e["Visible"],
138
+ )
86
139
 
87
- def plot_scatter(data, opt_s, xloc, ax):
140
+ def plot_scatter(data, opt_s, xloc, ax, label=None):
88
141
  scatter_positions = get_positions(
89
142
  xloc, opt_s["loc"], opt_s["x_width"], data.shape[0]
90
143
  )
91
144
  for i, (x, y) in enumerate(zip(scatter_positions.T, data.T)):
92
145
  color = to_rgba(opt_s["FaceColor"][i % len(opt_s["FaceColor"])])
93
- ax.scatter(
94
- x,
95
- y,
96
- color=color,
97
- alpha=opt_s["FaceAlpha"],
98
- edgecolor=opt_s["MarkerEdgeColor"],
99
- s=opt_s["MarkerSize"],
100
- marker=opt_s["Marker"],
101
- linewidths=opt_s["LineWidth"],
102
- cmap=opt_s["cmap"],
103
- )
146
+ if label is not None and i < len(label):
147
+ ax.scatter(
148
+ x,
149
+ y,
150
+ color=color,
151
+ alpha=opt_s["FaceAlpha"],
152
+ edgecolor=opt_s["MarkerEdgeColor"],
153
+ s=opt_s["MarkerSize"],
154
+ marker=opt_s["Marker"],
155
+ linewidths=opt_s["LineWidth"],
156
+ cmap=opt_s["cmap"],
157
+ label=label[i],
158
+ )
159
+ else:
160
+ ax.scatter(
161
+ x,
162
+ y,
163
+ color=color,
164
+ alpha=opt_s["FaceAlpha"],
165
+ edgecolor=opt_s["MarkerEdgeColor"],
166
+ s=opt_s["MarkerSize"],
167
+ marker=opt_s["Marker"],
168
+ linewidths=opt_s["LineWidth"],
169
+ cmap=opt_s["cmap"],
170
+ )
104
171
 
105
- def plot_boxplot(data, bx_opt, xloc, ax):
172
+ def plot_boxplot(data, bx_opt, xloc, ax, label=None):
106
173
  if "l" in bx_opt["loc"]:
107
174
  X_bx = xloc - bx_opt["x_width"]
108
175
  elif "r" in bx_opt["loc"]:
@@ -162,6 +229,7 @@ def catplot(data, *args, **kwargs):
162
229
  showmeans=bx_opt["MeanLine"],
163
230
  meanprops=meanprops,
164
231
  widths=bx_opt["x_width"],
232
+ label=label,
165
233
  )
166
234
 
167
235
  if bx_opt["BoxLineWidth"] < 0.1:
@@ -175,19 +243,84 @@ def catplot(data, *args, **kwargs):
175
243
  if bx_opt["MedianLineTop"]:
176
244
  ax.set_children(ax.get_children()[::-1]) # move median line forward
177
245
 
178
- def plot_violin(data, opt_v, xloc, ax):
246
+ def plot_violin(data, opt_v, xloc, ax, label=None):
179
247
  violin_positions = get_positions(
180
248
  xloc, opt_v["loc"], opt_v["x_width"], data.shape[0]
181
249
  )
182
250
  violin_positions = np.nanmean(violin_positions, axis=0)
183
251
  for i, (x, ys) in enumerate(zip(violin_positions, data.T)):
184
252
  ys = ys[~np.isnan(ys)]
185
- if len(ys) > 1:
186
- kde = gaussian_kde(ys, bw_method=opt_v["BandWidth"])
187
- min_val, max_val = ys.min(), ys.max()
188
- y_vals = np.linspace(min_val, max_val, opt_v["NumPoints"])
189
- kde_vals = kde(y_vals)
190
- kde_vals = kde_vals / kde_vals.max() * opt_v["x_width"]
253
+ kde = gaussian_kde(ys, bw_method=opt_v["BandWidth"])
254
+ min_val, max_val = ys.min(), ys.max()
255
+ y_vals = np.linspace(min_val, max_val, opt_v["NumPoints"])
256
+ kde_vals = kde(y_vals)
257
+ kde_vals = kde_vals / kde_vals.max() * opt_v["x_width"]
258
+ if label is not None and i < len(label):
259
+ if len(ys) > 1:
260
+ if "r" in opt_v["loc"].lower():
261
+ ax.fill_betweenx(
262
+ y_vals,
263
+ x,
264
+ x + kde_vals,
265
+ color=opt_v["FaceColor"][i % len(opt_v["FaceColor"])],
266
+ alpha=opt_v["FaceAlpha"],
267
+ edgecolor=opt_v["EdgeColor"],
268
+ label=label[i],
269
+ )
270
+ elif (
271
+ "l" in opt_v["loc"].lower() and not "f" in opt_v["loc"].lower()
272
+ ):
273
+ ax.fill_betweenx(
274
+ y_vals,
275
+ x - kde_vals,
276
+ x,
277
+ color=opt_v["FaceColor"][i % len(opt_v["FaceColor"])],
278
+ alpha=opt_v["FaceAlpha"],
279
+ edgecolor=opt_v["EdgeColor"],
280
+ label=label[i],
281
+ )
282
+ elif "o" in opt_v["loc"].lower() or "both" in opt_v["loc"].lower():
283
+ ax.fill_betweenx(
284
+ y_vals,
285
+ x - kde_vals,
286
+ x + kde_vals,
287
+ color=opt_v["FaceColor"][i % len(opt_v["FaceColor"])],
288
+ alpha=opt_v["FaceAlpha"],
289
+ edgecolor=opt_v["EdgeColor"],
290
+ label=label[i],
291
+ )
292
+ elif "i" in opt_v["loc"].lower():
293
+ if i % 2 == 1: # odd number
294
+ ax.fill_betweenx(
295
+ y_vals,
296
+ x - kde_vals,
297
+ x,
298
+ color=opt_v["FaceColor"][i % len(opt_v["FaceColor"])],
299
+ alpha=opt_v["FaceAlpha"],
300
+ edgecolor=opt_v["EdgeColor"],
301
+ label=label[i],
302
+ )
303
+ else:
304
+ ax.fill_betweenx(
305
+ y_vals,
306
+ x,
307
+ x + kde_vals,
308
+ color=opt_v["FaceColor"][i % len(opt_v["FaceColor"])],
309
+ alpha=opt_v["FaceAlpha"],
310
+ edgecolor=opt_v["EdgeColor"],
311
+ label=label[i],
312
+ )
313
+ elif "f" in opt_v["loc"].lower():
314
+ ax.fill_betweenx(
315
+ y_vals,
316
+ x - kde_vals,
317
+ x + kde_vals,
318
+ color=opt_v["FaceColor"][i % len(opt_v["FaceColor"])],
319
+ alpha=opt_v["FaceAlpha"],
320
+ edgecolor=opt_v["EdgeColor"],
321
+ label=label[i],
322
+ )
323
+ else:
191
324
  if "r" in opt_v["loc"].lower():
192
325
  ax.fill_betweenx(
193
326
  y_vals,
@@ -327,7 +460,7 @@ def catplot(data, *args, **kwargs):
327
460
  x = kwargs.get("x", None)
328
461
  y = kwargs.get("y", None)
329
462
  hue = kwargs.get("hue", None)
330
- data = df2array(data=data, x=x, y=y, hue=hue).T
463
+ data = df2array(data=data, x=x, y=y, hue=hue)
331
464
  xticklabels = []
332
465
  if hue is not None:
333
466
  for i in df[x].unique().tolist():
@@ -337,10 +470,12 @@ def catplot(data, *args, **kwargs):
337
470
  hue_len = len(df[hue].unique().tolist())
338
471
  xticks = generate_xticks_with_gap(x_len, hue_len)
339
472
  default_x_width = 0.85
473
+ legend_hue = df[hue].unique().tolist()
340
474
  else:
341
475
  for i in df[x].unique().tolist():
342
476
  xticklabels.append(i)
343
477
  xticks = np.arange(1, len(xticklabels) + 1)
478
+ legend_hue = xticklabels
344
479
  default_x_width = 0.5
345
480
  # when the xticklabels are too long, rotate the labels a bit
346
481
 
@@ -362,6 +497,10 @@ def catplot(data, *args, **kwargs):
362
497
  "xticklabels": xticklabels,
363
498
  "xangle": xangle,
364
499
  }
500
+ else:
501
+ xticks = np.arange(1, data.shape[1] + 1)
502
+ default_x_width = 0.5
503
+ xangle = 0
365
504
 
366
505
  # full_order
367
506
  opt = kwargs.get("opt", {})
@@ -520,17 +659,47 @@ def catplot(data, *args, **kwargs):
520
659
  else:
521
660
  xloc = opt["loc"]["xloc"]
522
661
  layers = sort_catplot_layers(opt["layer"])
662
+
663
+ label_which = kwargs.get("label_which", "barplot")
664
+ if "b" in label_which:
665
+ legend_which = "b"
666
+ elif "s" in label_which:
667
+ legend_which = "s"
668
+ elif "bx" in label_which:
669
+ legend_which = "bx"
670
+ elif "e" in label_which:
671
+ legend_which = "e"
672
+ elif "v" in label_which:
673
+ legend_which = "v"
674
+ else:
675
+ legend_which = None
676
+
523
677
  for layer in layers:
524
678
  if layer == "b" and opt["b"]["go"]:
525
- plot_bars(data, data_m, opt["b"], xloc, ax)
679
+ if legend_which == "b":
680
+ plot_bars(data, data_m, opt["b"], xloc, ax, label=legend_hue)
681
+ else:
682
+ plot_bars(data, data_m, opt["b"], xloc, ax, label=None)
526
683
  elif layer == "e" and opt["e"]["go"]:
527
- plot_errors(data, data_m, opt["e"], xloc, ax)
684
+ if legend_which == "e":
685
+ plot_errors(data, data_m, opt["e"], xloc, ax, label=legend_hue)
686
+ else:
687
+ plot_errors(data, data_m, opt["e"], xloc, ax, label=None)
528
688
  elif layer == "s" and opt["s"]["go"]:
529
- plot_scatter(data, opt["s"], xloc, ax)
689
+ if legend_which == "s":
690
+ plot_scatter(data, opt["s"], xloc, ax, label=legend_hue)
691
+ else:
692
+ plot_scatter(data, opt["s"], xloc, ax, label=None)
530
693
  elif layer == "bx" and opt["bx"]["go"]:
531
- plot_boxplot(data, opt["bx"], xloc, ax)
694
+ if legend_which == "bx":
695
+ plot_boxplot(data, opt["bx"], xloc, ax, label=legend_hue)
696
+ else:
697
+ plot_boxplot(data, opt["bx"], xloc, ax, label=None)
532
698
  elif layer == "v" and opt["v"]["go"]:
533
- plot_violin(data, opt["v"], xloc, ax)
699
+ if legend_which == "v":
700
+ plot_violin(data, opt["v"], xloc, ax, label=legend_hue)
701
+ else:
702
+ plot_violin(data, opt["v"], xloc, ax, label=None)
534
703
  elif all([layer == "l", opt["l"]["go"], opt["s"]["go"]]):
535
704
  plot_lines(data, opt["l"], opt["s"], ax)
536
705
 
@@ -539,98 +708,6 @@ def catplot(data, *args, **kwargs):
539
708
  return ax
540
709
 
541
710
 
542
- # from py2ls.ips import get_color,figsets
543
- # opt={}
544
- # opt = {
545
- # 'export':{'path':get_cwd()},
546
- # 'c': get_color(5,cmap='jet',by='linspace'), # Custom colors for 3 categories
547
- # 'b': {
548
- # 'go': 0,
549
- # 'x_width': 0.85,
550
- # 'FaceAlpha': 0.7,
551
- # 'EdgeColor':'none'
552
- # },
553
- # 'e': {
554
- # 'loc':'r',
555
- # 'go': 1,
556
- # 'error': 'sem',
557
- # 'Marker':'d',
558
- # 'CapSize': 1,
559
- # 'LineWidth':1,
560
- # 'CapLineWidth':8,
561
- # 'LineStyle':'--',
562
- # 'MarkerSize':6,
563
- # 'LineColor':'k',
564
- # 'FaceColor':get_color(10),
565
- # 'MarkerEdgeColor':'none',
566
- # 'Visible':True
567
- # },
568
- # 's': {
569
- # 'go': 1,
570
- # 'x_width':0.2,
571
- # 'loc':'random',
572
- # 'Marker': 'o',
573
- # # 'MarkerSize': 20,
574
- # 'FaceAlpha': 1,
575
- # 'FaceColor':'k',
576
- # 'LineWidth':1
577
-
578
- # },
579
- # 'bx':{
580
- # 'go':1,
581
- # 'FaceAlpha':0.8,
582
- # 'EdgeColor':'none',
583
- # 'loc':'c',
584
- # 'x_width':0.2,
585
- # 'WhiskerLineWidth':1,
586
- # 'MedianLineWidth':2,
587
- # # 'MedianLineColor':'r',
588
- # 'OutlierMarker':'+',
589
- # 'OutlierColor':'r',
590
- # 'CapSize':.2,
591
- # # 'Caps':False,
592
- # # 'CapLineColor':'r',
593
- # # 'CapLineWidth':8,
594
- # # 'MeanLine':True,
595
- # # 'FaceColor':['r','g','b','m','c']
596
- # },
597
- # 'v':{
598
- # 'go':0,
599
- # 'loc':'r',
600
- # 'x_width':0.2,
601
- # 'FaceAlpha':0.51,
602
- # },
603
- # 'l':{
604
- # 'go':1,
605
- # 'LineColor':'k'
606
- # }
607
- # }
608
- # data1 = np.random.rand(10, 5)
609
- # data2 = np.random.rand(10, 5)
610
- # fig, axs=plt.subplots(1,2,figsize=(6,2.5))
611
- # catplot(data1, opt=opt,ax=axs[0])
612
- # catplot(data2, opt=opt,ax=axs[1])
613
- # figsets(sp=5,
614
- # ax=axs[0],
615
- # xticks=np.arange(1,6,1),
616
- # xtickslabel=['glua1','glua2','a','b','c'],
617
- # xlabel='proteins',
618
- # xangle=90,
619
- # yticks=np.arange(0,2,0.5),
620
- # xlim=[0.75, 5.1],
621
- # ticks=dict(pad=1,c='k'))
622
-
623
- # figsave("/Users/macjianfeng/Dropbox/Downloads/",'test.pdf')
624
-
625
-
626
- import numpy as np
627
- import matplotlib
628
- import matplotlib.pyplot as plt
629
- import matplotlib.ticker as tck
630
- import seaborn as sns
631
- from cycler import cycler
632
-
633
-
634
711
  def get_cmap():
635
712
  return plt.colormaps()
636
713
 
@@ -1450,30 +1527,49 @@ def padcat(*args, fill_value=np.nan, axis=1, order="row"):
1450
1527
  The value to use for padding the shorter lists (default is np.nan).
1451
1528
  axis : int, optional
1452
1529
  The axis along which to concatenate (0 for rows, 1 for columns, default is 1).
1530
+ order : str, optional
1531
+ The order for flattening when required: "row" or "column" (default is "row").
1453
1532
 
1454
1533
  Returns:
1455
1534
  np.ndarray
1456
1535
  A 2D array with the input arrays concatenated along the specified axis,
1457
1536
  padded with fill_value where necessary.
1458
1537
  """
1459
- # Convert all inputs to 1D NumPy arrays
1538
+ # Set the order for processing
1460
1539
  if "ro" in order.lower():
1461
- order = "C" # in row
1540
+ order = "C" # row-major order
1462
1541
  else:
1463
- order = "F" # column
1464
- arrays = [np.asarray(arg).flatten(order=order) for arg in args]
1542
+ order = "F" # column-major order
1543
+
1544
+ # Process input arrays based on their dimensions
1545
+ processed_arrays = []
1546
+ for arg in args:
1547
+ arr = np.asarray(arg)
1548
+ if arr.ndim == 1:
1549
+ processed_arrays.append(arr) # Keep 1D arrays as is
1550
+ elif arr.ndim == 2:
1551
+ if axis == 0:
1552
+ # If concatenating along rows, split 2D arrays into 1D arrays row-wise
1553
+ processed_arrays.extend(arr)
1554
+ elif axis == 1:
1555
+ # If concatenating along columns, split 2D arrays into 1D arrays column-wise
1556
+ processed_arrays.extend(arr.T)
1557
+ else:
1558
+ raise ValueError("axis must be 0 or 1")
1559
+ else:
1560
+ raise ValueError("Input arrays must be 1D or 2D")
1465
1561
 
1466
1562
  if axis == 0:
1467
1563
  # Concatenate along rows
1468
- max_len = max(arr.size for arr in arrays)
1469
- result = np.full((len(arrays), max_len), fill_value)
1470
- for i, arr in enumerate(arrays):
1564
+ max_len = max(arr.size for arr in processed_arrays)
1565
+ result = np.full((len(processed_arrays), max_len), fill_value)
1566
+ for i, arr in enumerate(processed_arrays):
1471
1567
  result[i, : arr.size] = arr
1472
1568
  elif axis == 1:
1473
1569
  # Concatenate along columns
1474
- max_len = max(arr.size for arr in arrays)
1475
- result = np.full((max_len, len(arrays)), fill_value)
1476
- for i, arr in enumerate(arrays):
1570
+ max_len = max(arr.size for arr in processed_arrays)
1571
+ result = np.full((max_len, len(processed_arrays)), fill_value)
1572
+ for i, arr in enumerate(processed_arrays):
1477
1573
  result[: arr.size, i] = arr
1478
1574
  else:
1479
1575
  raise ValueError("axis must be 0 or 1")
@@ -1481,49 +1577,77 @@ def padcat(*args, fill_value=np.nan, axis=1, order="row"):
1481
1577
  return result
1482
1578
 
1483
1579
 
1484
- def sort_rows_move_nan(arr):
1580
+ # # Example usage:
1581
+ # a = [1, np.nan]
1582
+ # b = [1, 3, 4, np.nan, 2, np.nan]
1583
+ # c = [1, 2, 3, 4, 5, 6, 7, 8, 10]
1584
+ # d = padcat(a, b)
1585
+ # result1 = padcat(d, c)
1586
+ # result2 = padcat(a, b, c)
1587
+ # print("Result of padcat(d, c):\n", result1)
1588
+ # print("Result of padcat(a, b, c):\n", result2)
1589
+
1590
+
1591
+ def sort_rows_move_nan(arr, sort=False):
1485
1592
  # Handle edge cases where all values are NaN
1486
1593
  if np.all(np.isnan(arr)):
1487
1594
  return arr # Return unchanged if the entire array is NaN
1488
1595
 
1489
- # Replace NaNs with a temporary large value for sorting
1490
- temp_value = (
1491
- np.nanmax(arr[np.isfinite(arr)]) + 1 if np.any(np.isfinite(arr)) else np.inf
1492
- )
1493
- arr_no_nan = np.where(np.isnan(arr), temp_value, arr)
1494
-
1495
- # Sort each row
1496
- sorted_arr = np.sort(arr_no_nan, axis=1)
1596
+ if sort:
1597
+ # Replace NaNs with a temporary large value for sorting
1598
+ temp_value = (
1599
+ np.nanmax(arr[np.isfinite(arr)]) + 1 if np.any(np.isfinite(arr)) else np.inf
1600
+ )
1601
+ arr_no_nan = np.where(np.isnan(arr), temp_value, arr)
1497
1602
 
1498
- # Move NaNs to the end
1499
- result_arr = np.where(sorted_arr == temp_value, np.nan, sorted_arr)
1603
+ # Sort each row
1604
+ sorted_arr = np.sort(arr_no_nan, axis=1)
1500
1605
 
1501
- # Remove rows that contain only NaNs
1606
+ # Move NaNs to the end
1607
+ result_arr = np.where(sorted_arr == temp_value, np.nan, sorted_arr)
1608
+ else:
1609
+ result_rows = []
1610
+ for row in arr:
1611
+ # Separate non-NaN and NaN values
1612
+ non_nan_values = row[~np.isnan(row)]
1613
+ nan_count = np.isnan(row).sum()
1614
+ # Create a new row with non-NaN values followed by NaNs
1615
+ new_row = np.concatenate([non_nan_values, [np.nan] * nan_count])
1616
+ result_rows.append(new_row)
1617
+ # Convert the list of rows back into a 2D NumPy array
1618
+ result_arr = np.array(result_rows)
1619
+
1620
+ # Remove rows/columns that contain only NaNs
1502
1621
  clean_arr = result_arr[~np.isnan(result_arr).all(axis=1)]
1503
-
1504
- # Remove columns that contain only NaNs
1505
1622
  clean_arr_ = clean_arr[:, ~np.isnan(clean_arr).all(axis=0)]
1506
1623
 
1507
1624
  return clean_arr_
1508
1625
 
1509
1626
 
1510
- def df2array(data: pd.DataFrame, x, y, hue=None):
1627
+ def df2array(data: pd.DataFrame, x, y, hue=None, sort=False):
1511
1628
  if hue is None:
1512
1629
  a = []
1513
- cat_x = data[x].unique().tolist()
1630
+ if sort:
1631
+ np.sort(data[x].unique().tolist()).tolist()
1632
+ else:
1633
+ cat_x = data[x].unique().tolist()
1514
1634
  for i, x_ in enumerate(cat_x):
1515
1635
  new_ = data.loc[data[x] == x_, y].to_list()
1516
1636
  a = padcat(a, new_, axis=0)
1517
- return sort_rows_move_nan(a.reshape(2**i, -1))
1637
+ return sort_rows_move_nan(a).T
1518
1638
  else:
1519
1639
  a = []
1520
- cat_x = data[x].unique().tolist()
1521
- cat_hue = data[hue].unique().tolist()
1640
+ if sort:
1641
+ cat_x = np.sort(data[x].unique().tolist()).tolist()
1642
+ cat_hue = np.sort(data[hue].unique().tolist()).tolist()
1643
+ else:
1644
+ cat_x = data[x].unique().tolist()
1645
+ cat_hue = data[hue].unique().tolist()
1522
1646
  for i, x_ in enumerate(cat_x):
1523
1647
  for j, hue_ in enumerate(cat_hue):
1524
1648
  new_ = data.loc[(data[x] == x_) & (data[hue] == hue_), y].to_list()
1525
1649
  a = padcat(a, new_, axis=0)
1526
- return sort_rows_move_nan(a.reshape(2 ** ((i + 1) * (j + 1)), -1))
1650
+ return sort_rows_move_nan(a).T
1527
1651
 
1528
1652
 
1529
1653
  def generate_xticks_with_gap(x_len, hue_len):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: py2ls
3
- Version: 0.1.8.6
3
+ Version: 0.1.8.8
4
4
  Summary: py(thon)2(too)ls
5
5
  Author: Jianfeng
6
6
  Author-email: Jianfeng.Liu0413@gmail.com
@@ -136,12 +136,12 @@ py2ls/export_requirements.py,sha256=x2WgUF0jYKz9GfA1MVKN-MdsM-oQ8yUeC6Ua8oCymio,
136
136
  py2ls/freqanalysis.py,sha256=F4218VSPbgL5tnngh6xNCYuNnfR-F_QjECUUxrPYZss,32594
137
137
  py2ls/ips.py,sha256=6eNvNwaCDj2jyjter7VPL9oEGB2jS4ge9mSgiIrvZfo,100788
138
138
  py2ls/netfinder.py,sha256=OMStrwMAASf1ajlyEfseoaEygo7G5WKBAFRE0LY15Lw,49477
139
- py2ls/plot.py,sha256=drlrWMUseSqkaTQHeTygypn39SUiovYuvThLQZ1Df_Y,55682
139
+ py2ls/plot.py,sha256=E6cV26ixVa8HFYTsTvaEtc-CgLYvaA7OhPg6S0eBZ2M,62740
140
140
  py2ls/setuptools-70.1.0-py3-none-any.whl,sha256=2bi3cUVal8ip86s0SOvgspteEF8SKLukECi-EWmFomc,882588
141
141
  py2ls/sleep_events_detectors.py,sha256=bQA3HJqv5qnYKJJEIhCyhlDtkXQfIzqksnD0YRXso68,52145
142
142
  py2ls/stats.py,sha256=Wd9yCKQ_61QD29WMEgMuEcreFxF91NmlPW65iWT2B5w,39041
143
143
  py2ls/translator.py,sha256=bc5FB-wqC4TtQz9gyCP1mE38HqNRJ_pmuRIgKnAlMzM,30581
144
144
  py2ls/wb_detector.py,sha256=7y6TmBUj9exCZeIgBAJ_9hwuhkDh1x_-yg4dvNY1_GQ,6284
145
- py2ls-0.1.8.6.dist-info/METADATA,sha256=CCDGfjYmrUoLuYqvwidE8OkCBWs-EvCa5tviSzKVOjs,20017
146
- py2ls-0.1.8.6.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
147
- py2ls-0.1.8.6.dist-info/RECORD,,
145
+ py2ls-0.1.8.8.dist-info/METADATA,sha256=tnUzLUWK94WS_DVJ1Jxcn_fRFrQRktuceHW0WMRfS5g,20017
146
+ py2ls-0.1.8.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
147
+ py2ls-0.1.8.8.dist-info/RECORD,,