py2ls 0.1.8.4__py3-none-any.whl → 0.1.8.6__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
@@ -1,5 +1,6 @@
1
1
  import matplotlib.pyplot as plt
2
2
  import numpy as np
3
+ import pandas as pd
3
4
  from matplotlib.colors import to_rgba
4
5
  from scipy.stats import gaussian_kde
5
6
 
@@ -318,6 +319,50 @@ def catplot(data, *args, **kwargs):
318
319
  # custom_order = ['s', 'bx', 'e']
319
320
  # full_order = sort_catplot_layers(custom_order)
320
321
 
322
+ # figsets
323
+ kw_figsets = kwargs.get("figsets", None)
324
+ # check the data type
325
+ if isinstance(data, pd.DataFrame):
326
+ df = data.copy()
327
+ x = kwargs.get("x", None)
328
+ y = kwargs.get("y", None)
329
+ hue = kwargs.get("hue", None)
330
+ data = df2array(data=data, x=x, y=y, hue=hue).T
331
+ xticklabels = []
332
+ if hue is not None:
333
+ for i in df[x].unique().tolist():
334
+ for j in df[hue].unique().tolist():
335
+ xticklabels.append(i + "-" + j)
336
+ x_len = len(df[x].unique().tolist())
337
+ hue_len = len(df[hue].unique().tolist())
338
+ xticks = generate_xticks_with_gap(x_len, hue_len)
339
+ default_x_width = 0.85
340
+ else:
341
+ for i in df[x].unique().tolist():
342
+ xticklabels.append(i)
343
+ xticks = np.arange(1, len(xticklabels) + 1)
344
+ default_x_width = 0.5
345
+ # when the xticklabels are too long, rotate the labels a bit
346
+
347
+ xangle = 30 if max([len(i) for i in xticklabels]) > 5 else 0
348
+ if kw_figsets is not None:
349
+ kw_figsets = {
350
+ "ylabel": y,
351
+ "xlabel": x,
352
+ "xticks": xticks,
353
+ "xticklabels": xticklabels,
354
+ "xangle": xangle,
355
+ **kw_figsets,
356
+ }
357
+ else:
358
+ kw_figsets = {
359
+ "ylabel": y,
360
+ "xlabel": x,
361
+ "xticks": xticks,
362
+ "xticklabels": xticklabels,
363
+ "xangle": xangle,
364
+ }
365
+
321
366
  # full_order
322
367
  opt = kwargs.get("opt", {})
323
368
  ax = kwargs.get("ax", None)
@@ -347,7 +392,7 @@ def catplot(data, *args, **kwargs):
347
392
 
348
393
  opt.setdefault("loc", {})
349
394
  opt["loc"].setdefault("go", 0)
350
- opt["loc"].setdefault("xloc", np.arange(1, data.shape[1] + 1))
395
+ opt["loc"].setdefault("xloc", xticks)
351
396
 
352
397
  # export setting
353
398
  opt.setdefault("export", {})
@@ -366,7 +411,7 @@ def catplot(data, *args, **kwargs):
366
411
  opt["b"].setdefault("EdgeAlpha", 1)
367
412
  opt["b"].setdefault("LineStyle", "-")
368
413
  opt["b"].setdefault("LineWidth", 0.8)
369
- opt["b"].setdefault("x_width", 0.5)
414
+ opt["b"].setdefault("x_width", default_x_width)
370
415
  opt["b"].setdefault("ShowBaseLine", "off")
371
416
  opt["b"].setdefault("hatch", None)
372
417
 
@@ -470,7 +515,10 @@ def catplot(data, *args, **kwargs):
470
515
  opt[key].update(kwargs[key])
471
516
  else:
472
517
  opt[key] = kwargs[key]
473
- xloc = opt["loc"]["xloc"]
518
+ if isinstance(opt["loc"]["xloc"], list):
519
+ xloc = np.array(opt["loc"]["xloc"])
520
+ else:
521
+ xloc = opt["loc"]["xloc"]
474
522
  layers = sort_catplot_layers(opt["layer"])
475
523
  for layer in layers:
476
524
  if layer == "b" and opt["b"]["go"]:
@@ -485,8 +533,7 @@ def catplot(data, *args, **kwargs):
485
533
  plot_violin(data, opt["v"], xloc, ax)
486
534
  elif all([layer == "l", opt["l"]["go"], opt["s"]["go"]]):
487
535
  plot_lines(data, opt["l"], opt["s"], ax)
488
- # figsets
489
- kw_figsets = kwargs.get("figsets", None)
536
+
490
537
  if kw_figsets is not None:
491
538
  figsets(ax=ax, **kw_figsets)
492
539
  return ax
@@ -1357,7 +1404,42 @@ def add_colorbar(im, width=None, pad=None, **kwargs):
1357
1404
  return fig.colorbar(im, cax=cax, **kwargs) # draw cbar
1358
1405
 
1359
1406
 
1360
- def padcat(*args, fill_value=np.nan, axis=1):
1407
+ # def padcat(*args, fill_value=np.nan, axis=1):
1408
+ # """
1409
+ # Concatenate vectors with padding.
1410
+
1411
+ # Parameters:
1412
+ # *args : variable number of list or 1D arrays
1413
+ # Input arrays to concatenate.
1414
+ # fill_value : scalar, optional
1415
+ # The value to use for padding the shorter lists (default is np.nan).
1416
+ # axis : int, optional
1417
+ # The axis along which to concatenate (0 for rows, 1 for columns, default is 0).
1418
+
1419
+ # Returns:
1420
+ # np.ndarray
1421
+ # A 2D array with the input arrays concatenated along the specified axis, padded with fill_value where necessary.
1422
+ # """
1423
+ # if axis == 0:
1424
+ # # Concatenate along rows
1425
+ # max_len = max(len(lst) for lst in args)
1426
+ # result = np.full((len(args), max_len), fill_value)
1427
+ # for i, lst in enumerate(args):
1428
+ # result[i, : len(lst)] = lst
1429
+ # elif axis == 1:
1430
+ # # Concatenate along columns
1431
+ # max_len = max(len(lst) for lst in args)
1432
+ # result = np.full((max_len, len(args)), fill_value)
1433
+ # for i, lst in enumerate(args):
1434
+ # result[: len(lst), i] = lst
1435
+ # else:
1436
+ # raise ValueError("axis must be 0 or 1")
1437
+
1438
+ # return result
1439
+ import numpy as np
1440
+
1441
+
1442
+ def padcat(*args, fill_value=np.nan, axis=1, order="row"):
1361
1443
  """
1362
1444
  Concatenate vectors with padding.
1363
1445
 
@@ -1367,25 +1449,101 @@ def padcat(*args, fill_value=np.nan, axis=1):
1367
1449
  fill_value : scalar, optional
1368
1450
  The value to use for padding the shorter lists (default is np.nan).
1369
1451
  axis : int, optional
1370
- The axis along which to concatenate (0 for rows, 1 for columns, default is 0).
1452
+ The axis along which to concatenate (0 for rows, 1 for columns, default is 1).
1371
1453
 
1372
1454
  Returns:
1373
1455
  np.ndarray
1374
- A 2D array with the input arrays concatenated along the specified axis, padded with fill_value where necessary.
1456
+ A 2D array with the input arrays concatenated along the specified axis,
1457
+ padded with fill_value where necessary.
1375
1458
  """
1459
+ # Convert all inputs to 1D NumPy arrays
1460
+ if "ro" in order.lower():
1461
+ order = "C" # in row
1462
+ else:
1463
+ order = "F" # column
1464
+ arrays = [np.asarray(arg).flatten(order=order) for arg in args]
1465
+
1376
1466
  if axis == 0:
1377
1467
  # Concatenate along rows
1378
- max_len = max(len(lst) for lst in args)
1379
- result = np.full((len(args), max_len), fill_value)
1380
- for i, lst in enumerate(args):
1381
- result[i, : len(lst)] = lst
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):
1471
+ result[i, : arr.size] = arr
1382
1472
  elif axis == 1:
1383
1473
  # Concatenate along columns
1384
- max_len = max(len(lst) for lst in args)
1385
- result = np.full((max_len, len(args)), fill_value)
1386
- for i, lst in enumerate(args):
1387
- result[: len(lst), i] = lst
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):
1477
+ result[: arr.size, i] = arr
1388
1478
  else:
1389
1479
  raise ValueError("axis must be 0 or 1")
1390
1480
 
1391
1481
  return result
1482
+
1483
+
1484
+ def sort_rows_move_nan(arr):
1485
+ # Handle edge cases where all values are NaN
1486
+ if np.all(np.isnan(arr)):
1487
+ return arr # Return unchanged if the entire array is NaN
1488
+
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)
1497
+
1498
+ # Move NaNs to the end
1499
+ result_arr = np.where(sorted_arr == temp_value, np.nan, sorted_arr)
1500
+
1501
+ # Remove rows that contain only NaNs
1502
+ clean_arr = result_arr[~np.isnan(result_arr).all(axis=1)]
1503
+
1504
+ # Remove columns that contain only NaNs
1505
+ clean_arr_ = clean_arr[:, ~np.isnan(clean_arr).all(axis=0)]
1506
+
1507
+ return clean_arr_
1508
+
1509
+
1510
+ def df2array(data: pd.DataFrame, x, y, hue=None):
1511
+ if hue is None:
1512
+ a = []
1513
+ cat_x = data[x].unique().tolist()
1514
+ for i, x_ in enumerate(cat_x):
1515
+ new_ = data.loc[data[x] == x_, y].to_list()
1516
+ a = padcat(a, new_, axis=0)
1517
+ return sort_rows_move_nan(a.reshape(2**i, -1))
1518
+ else:
1519
+ a = []
1520
+ cat_x = data[x].unique().tolist()
1521
+ cat_hue = data[hue].unique().tolist()
1522
+ for i, x_ in enumerate(cat_x):
1523
+ for j, hue_ in enumerate(cat_hue):
1524
+ new_ = data.loc[(data[x] == x_) & (data[hue] == hue_), y].to_list()
1525
+ a = padcat(a, new_, axis=0)
1526
+ return sort_rows_move_nan(a.reshape(2 ** ((i + 1) * (j + 1)), -1))
1527
+
1528
+
1529
+ def generate_xticks_with_gap(x_len, hue_len):
1530
+ """
1531
+ Generate a concatenated array based on x_len and hue_len,
1532
+ and return only the positive numbers.
1533
+
1534
+ Parameters:
1535
+ - x_len: int, number of segments to generate
1536
+ - hue_len: int, length of each hue
1537
+
1538
+ Returns:
1539
+ - numpy array: Concatenated array containing only positive numbers
1540
+ """
1541
+
1542
+ arrays = [
1543
+ np.arange(1, hue_len + 1) + hue_len * (x_len - i) + (x_len - i)
1544
+ for i in range(max(x_len, hue_len), 0, -1) # i iterates from 3 to 1
1545
+ ]
1546
+ concatenated_array = np.concatenate(arrays)
1547
+ positive_array = concatenated_array[concatenated_array > 0]
1548
+
1549
+ return positive_array
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: py2ls
3
- Version: 0.1.8.4
3
+ Version: 0.1.8.6
4
4
  Summary: py(thon)2(too)ls
5
5
  Author: Jianfeng
6
6
  Author-email: Jianfeng.Liu0413@gmail.com
@@ -134,14 +134,14 @@ py2ls/db2ls.py,sha256=MMfFX47aIPIyu7fU9aPvX9lbPRPYOpJ_VXwlnWk-8qo,13615
134
134
  py2ls/doc.py,sha256=xN3g1OWfoaGUhikbJ0NqbN5eKy1VZVvWwRlhHMgyVEc,4243
135
135
  py2ls/export_requirements.py,sha256=x2WgUF0jYKz9GfA1MVKN-MdsM-oQ8yUeC6Ua8oCymio,2325
136
136
  py2ls/freqanalysis.py,sha256=F4218VSPbgL5tnngh6xNCYuNnfR-F_QjECUUxrPYZss,32594
137
- py2ls/ips.py,sha256=mBFTARM6Pvj5EaVRR05CXCBlyNcbX2JfMQWBjnxONjI,100773
138
- py2ls/netfinder.py,sha256=MY_0TQY_zaRBZ6wfR4RxNCGrU93HFmDVDRRy1EXl75o,47566
139
- py2ls/plot.py,sha256=WIAUDd4cRjFTX_Y_SWwsSw9_gIQF_Ye6R_-MvhB9G1k,50437
137
+ py2ls/ips.py,sha256=6eNvNwaCDj2jyjter7VPL9oEGB2jS4ge9mSgiIrvZfo,100788
138
+ py2ls/netfinder.py,sha256=OMStrwMAASf1ajlyEfseoaEygo7G5WKBAFRE0LY15Lw,49477
139
+ py2ls/plot.py,sha256=drlrWMUseSqkaTQHeTygypn39SUiovYuvThLQZ1Df_Y,55682
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.4.dist-info/METADATA,sha256=ieFZ6Fgi3Tw6gIm1kdxkpLbk3XMgEwJ7uUIZTjSr-eo,20017
146
- py2ls-0.1.8.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
147
- py2ls-0.1.8.4.dist-info/RECORD,,
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,,