py2ls 0.1.9.1__py3-none-any.whl → 0.1.9.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
py2ls/plot.py
CHANGED
@@ -10,6 +10,7 @@ from cycler import cycler
|
|
10
10
|
import logging
|
11
11
|
import os
|
12
12
|
from .ips import fsave, fload, mkdir
|
13
|
+
from .stats import *
|
13
14
|
|
14
15
|
# Suppress INFO messages from fontTools
|
15
16
|
logging.getLogger("fontTools").setLevel(logging.WARNING)
|
@@ -220,8 +221,11 @@ def catplot(data, *args, **kwargs):
|
|
220
221
|
# MeanLine or MedianLine only keep only one
|
221
222
|
if bx_opt["MeanLine"]: # MeanLine has priority
|
222
223
|
bx_opt["MedianLine"] = False
|
224
|
+
# rm NaNs
|
225
|
+
cleaned_data = [data[~np.isnan(data[:, i]), i] for i in range(data.shape[1])]
|
226
|
+
|
223
227
|
bxp = ax.boxplot(
|
224
|
-
|
228
|
+
cleaned_data,
|
225
229
|
positions=X_bx,
|
226
230
|
notch=bx_opt["Notch"],
|
227
231
|
patch_artist=True,
|
@@ -488,7 +492,33 @@ def catplot(data, *args, **kwargs):
|
|
488
492
|
default_x_width = 0.85
|
489
493
|
legend_hue = df[hue].unique().tolist()
|
490
494
|
default_colors = get_color(hue_len)
|
495
|
+
|
496
|
+
# ! stats info
|
497
|
+
stats_param = kwargs.get("stats", False)
|
498
|
+
res = pd.DataFrame() # Initialize an empty DataFrame to store results
|
499
|
+
for i in df[x].unique().tolist():
|
500
|
+
print(i)
|
501
|
+
if hue and stats_param:
|
502
|
+
if isinstance(stats_param, dict):
|
503
|
+
if "factor" in stats_param.keys():
|
504
|
+
res_tmp = FuncMultiCmpt(data=df, dv=y, **stats_param)
|
505
|
+
else:
|
506
|
+
res_tmp = FuncMultiCmpt(
|
507
|
+
data=df[df[x] == i], dv=y, factor=hue, **stats_param
|
508
|
+
)
|
509
|
+
elif bool(stats_param):
|
510
|
+
res_tmp = FuncMultiCmpt(data=df, dv=y, factor=hue)
|
511
|
+
else:
|
512
|
+
res_tmp = "did not work properly"
|
513
|
+
display_output(res_tmp)
|
514
|
+
res_tmp = [{"x": i, **res_tmp}]
|
515
|
+
res = pd.concat(
|
516
|
+
[res, pd.DataFrame([res_tmp])], ignore_index=True
|
517
|
+
)
|
518
|
+
display_output(res)
|
491
519
|
else:
|
520
|
+
# ! stats info
|
521
|
+
stats_param = kwargs.get("stats", False)
|
492
522
|
for i in df[x].unique().tolist():
|
493
523
|
xticklabels.append(i)
|
494
524
|
xticks = np.arange(1, len(xticklabels) + 1).tolist()
|
@@ -496,9 +526,17 @@ def catplot(data, *args, **kwargs):
|
|
496
526
|
legend_hue = xticklabels
|
497
527
|
default_colors = get_color(len(xticklabels))
|
498
528
|
default_x_width = 0.5
|
529
|
+
res = None
|
530
|
+
if x and stats_param:
|
531
|
+
if isinstance(stats_param, dict):
|
532
|
+
res = FuncMultiCmpt(data=df, dv=y, factor=x, **stats_param)
|
533
|
+
elif bool(stats_param):
|
534
|
+
res = FuncMultiCmpt(data=df, dv=y, factor=x)
|
535
|
+
else:
|
536
|
+
res = "did not work properly"
|
537
|
+
display_output(res)
|
499
538
|
|
500
539
|
# when the xticklabels are too long, rotate the labels a bit
|
501
|
-
|
502
540
|
xangle = 30 if max([len(i) for i in xticklabels]) > 50 else 0
|
503
541
|
if kw_figsets is not None:
|
504
542
|
kw_figsets = {
|
@@ -526,6 +564,22 @@ def catplot(data, *args, **kwargs):
|
|
526
564
|
|
527
565
|
# full_order
|
528
566
|
opt = kwargs.get("opt", {})
|
567
|
+
|
568
|
+
# load style:
|
569
|
+
style_use = None
|
570
|
+
for k, v in kwargs.items():
|
571
|
+
if "style" in k and "exp" not in k:
|
572
|
+
style_use = v
|
573
|
+
break
|
574
|
+
if style_use:
|
575
|
+
try:
|
576
|
+
dir_curr_script = os.path.dirname(os.path.abspath(__file__))
|
577
|
+
dir_style = dir_curr_script + "/data/styles/"
|
578
|
+
style_load = fload(dir_style + style_use + ".json")
|
579
|
+
style_load = remove_colors_in_dict(style_load)
|
580
|
+
opt.update(style_load)
|
581
|
+
except:
|
582
|
+
print(f"cannot find the style'{style_name}'")
|
529
583
|
ax = kwargs.get("ax", None)
|
530
584
|
if "ax" not in locals() or ax is None:
|
531
585
|
ax = plt.gca()
|
@@ -654,16 +708,6 @@ def catplot(data, *args, **kwargs):
|
|
654
708
|
opt["v"].setdefault("NumPoints", 500)
|
655
709
|
opt["v"].setdefault("BoundaryCorrection", "reflection")
|
656
710
|
|
657
|
-
# load style:
|
658
|
-
style_use = kwargs.get("style_use", None)
|
659
|
-
if style_use:
|
660
|
-
try:
|
661
|
-
dir_curr_script = os.path.dirname(os.path.abspath(__file__))
|
662
|
-
dir_style = dir_curr_script + "/data/styles/"
|
663
|
-
style_load = fload(dir_style + style_use + ".json")
|
664
|
-
opt.update(style_load)
|
665
|
-
except:
|
666
|
-
print(f"cannot find the style'{style_name}'")
|
667
711
|
data_m = np.nanmean(data, axis=0)
|
668
712
|
nr, nc = data.shape
|
669
713
|
|
@@ -692,7 +736,6 @@ def catplot(data, *args, **kwargs):
|
|
692
736
|
legend_which = "v"
|
693
737
|
else:
|
694
738
|
legend_which = None
|
695
|
-
|
696
739
|
for layer in layers:
|
697
740
|
if layer == "b" and opt["b"]["go"]:
|
698
741
|
if legend_which == "b":
|
@@ -721,8 +764,7 @@ def catplot(data, *args, **kwargs):
|
|
721
764
|
plot_violin(data, opt["v"], xloc, ax, label=None)
|
722
765
|
elif all([layer == "l", opt["l"]["go"], opt["s"]["go"]]):
|
723
766
|
plot_lines(data, opt["l"], opt["s"], ax)
|
724
|
-
|
725
|
-
print("layers run some problems")
|
767
|
+
|
726
768
|
if kw_figsets is not None:
|
727
769
|
figsets(ax=ax, **kw_figsets)
|
728
770
|
show_legend = kwargs.get("show_legend", True)
|
@@ -734,6 +776,7 @@ def catplot(data, *args, **kwargs):
|
|
734
776
|
dir_curr_script = os.path.dirname(os.path.abspath(__file__))
|
735
777
|
dir_style = dir_curr_script + "/data/styles/"
|
736
778
|
fsave(dir_style + style_export + ".json", opt)
|
779
|
+
|
737
780
|
return ax, opt
|
738
781
|
else:
|
739
782
|
col_names = data[col].unique().tolist()
|
@@ -750,7 +793,10 @@ def catplot(data, *args, **kwargs):
|
|
750
793
|
if i < len(col_names):
|
751
794
|
df_sub = data.loc[data[col] == col_names[i]]
|
752
795
|
_, opt = catplot(ax=ax, data=df_sub, **kwargs)
|
753
|
-
ax.set_title(col_names[i])
|
796
|
+
ax.set_title(f"{col}={col_names[i]}")
|
797
|
+
x_label = kwargs.get("x", None)
|
798
|
+
if x_label:
|
799
|
+
ax.set_xlabel(x_label)
|
754
800
|
print(f"Axis layout shape: {axs.shape}")
|
755
801
|
return axs, opt
|
756
802
|
|
@@ -1530,175 +1576,6 @@ def add_colorbar(im, width=None, pad=None, **kwargs):
|
|
1530
1576
|
return fig.colorbar(im, cax=cax, **kwargs) # draw cbar
|
1531
1577
|
|
1532
1578
|
|
1533
|
-
# def padcat(*args, fill_value=np.nan, axis=1):
|
1534
|
-
# """
|
1535
|
-
# Concatenate vectors with padding.
|
1536
|
-
|
1537
|
-
# Parameters:
|
1538
|
-
# *args : variable number of list or 1D arrays
|
1539
|
-
# Input arrays to concatenate.
|
1540
|
-
# fill_value : scalar, optional
|
1541
|
-
# The value to use for padding the shorter lists (default is np.nan).
|
1542
|
-
# axis : int, optional
|
1543
|
-
# The axis along which to concatenate (0 for rows, 1 for columns, default is 0).
|
1544
|
-
|
1545
|
-
# Returns:
|
1546
|
-
# np.ndarray
|
1547
|
-
# A 2D array with the input arrays concatenated along the specified axis, padded with fill_value where necessary.
|
1548
|
-
# """
|
1549
|
-
# if axis == 0:
|
1550
|
-
# # Concatenate along rows
|
1551
|
-
# max_len = max(len(lst) for lst in args)
|
1552
|
-
# result = np.full((len(args), max_len), fill_value)
|
1553
|
-
# for i, lst in enumerate(args):
|
1554
|
-
# result[i, : len(lst)] = lst
|
1555
|
-
# elif axis == 1:
|
1556
|
-
# # Concatenate along columns
|
1557
|
-
# max_len = max(len(lst) for lst in args)
|
1558
|
-
# result = np.full((max_len, len(args)), fill_value)
|
1559
|
-
# for i, lst in enumerate(args):
|
1560
|
-
# result[: len(lst), i] = lst
|
1561
|
-
# else:
|
1562
|
-
# raise ValueError("axis must be 0 or 1")
|
1563
|
-
|
1564
|
-
# return result
|
1565
|
-
import numpy as np
|
1566
|
-
|
1567
|
-
|
1568
|
-
def padcat(*args, fill_value=np.nan, axis=1, order="row"):
|
1569
|
-
"""
|
1570
|
-
Concatenate vectors with padding.
|
1571
|
-
|
1572
|
-
Parameters:
|
1573
|
-
*args : variable number of list or 1D arrays
|
1574
|
-
Input arrays to concatenate.
|
1575
|
-
fill_value : scalar, optional
|
1576
|
-
The value to use for padding the shorter lists (default is np.nan).
|
1577
|
-
axis : int, optional
|
1578
|
-
The axis along which to concatenate (0 for rows, 1 for columns, default is 1).
|
1579
|
-
order : str, optional
|
1580
|
-
The order for flattening when required: "row" or "column" (default is "row").
|
1581
|
-
|
1582
|
-
Returns:
|
1583
|
-
np.ndarray
|
1584
|
-
A 2D array with the input arrays concatenated along the specified axis,
|
1585
|
-
padded with fill_value where necessary.
|
1586
|
-
"""
|
1587
|
-
# Set the order for processing
|
1588
|
-
if "ro" in order.lower():
|
1589
|
-
order = "C" # row-major order
|
1590
|
-
else:
|
1591
|
-
order = "F" # column-major order
|
1592
|
-
|
1593
|
-
# Process input arrays based on their dimensions
|
1594
|
-
processed_arrays = []
|
1595
|
-
for arg in args:
|
1596
|
-
arr = np.asarray(arg)
|
1597
|
-
if arr.ndim == 1:
|
1598
|
-
processed_arrays.append(arr) # Keep 1D arrays as is
|
1599
|
-
elif arr.ndim == 2:
|
1600
|
-
if axis == 0:
|
1601
|
-
# If concatenating along rows, split 2D arrays into 1D arrays row-wise
|
1602
|
-
processed_arrays.extend(arr)
|
1603
|
-
elif axis == 1:
|
1604
|
-
# If concatenating along columns, split 2D arrays into 1D arrays column-wise
|
1605
|
-
processed_arrays.extend(arr.T)
|
1606
|
-
else:
|
1607
|
-
raise ValueError("axis must be 0 or 1")
|
1608
|
-
else:
|
1609
|
-
raise ValueError("Input arrays must be 1D or 2D")
|
1610
|
-
|
1611
|
-
if axis == 0:
|
1612
|
-
# Concatenate along rows
|
1613
|
-
max_len = max(arr.size for arr in processed_arrays)
|
1614
|
-
result = np.full((len(processed_arrays), max_len), fill_value)
|
1615
|
-
for i, arr in enumerate(processed_arrays):
|
1616
|
-
result[i, : arr.size] = arr
|
1617
|
-
elif axis == 1:
|
1618
|
-
# Concatenate along columns
|
1619
|
-
max_len = max(arr.size for arr in processed_arrays)
|
1620
|
-
result = np.full((max_len, len(processed_arrays)), fill_value)
|
1621
|
-
for i, arr in enumerate(processed_arrays):
|
1622
|
-
result[: arr.size, i] = arr
|
1623
|
-
else:
|
1624
|
-
raise ValueError("axis must be 0 or 1")
|
1625
|
-
|
1626
|
-
return result
|
1627
|
-
|
1628
|
-
|
1629
|
-
# # Example usage:
|
1630
|
-
# a = [1, np.nan]
|
1631
|
-
# b = [1, 3, 4, np.nan, 2, np.nan]
|
1632
|
-
# c = [1, 2, 3, 4, 5, 6, 7, 8, 10]
|
1633
|
-
# d = padcat(a, b)
|
1634
|
-
# result1 = padcat(d, c)
|
1635
|
-
# result2 = padcat(a, b, c)
|
1636
|
-
# print("Result of padcat(d, c):\n", result1)
|
1637
|
-
# print("Result of padcat(a, b, c):\n", result2)
|
1638
|
-
|
1639
|
-
|
1640
|
-
def sort_rows_move_nan(arr, sort=False):
|
1641
|
-
# Handle edge cases where all values are NaN
|
1642
|
-
if np.all(np.isnan(arr)):
|
1643
|
-
return arr # Return unchanged if the entire array is NaN
|
1644
|
-
|
1645
|
-
if sort:
|
1646
|
-
# Replace NaNs with a temporary large value for sorting
|
1647
|
-
temp_value = (
|
1648
|
-
np.nanmax(arr[np.isfinite(arr)]) + 1 if np.any(np.isfinite(arr)) else np.inf
|
1649
|
-
)
|
1650
|
-
arr_no_nan = np.where(np.isnan(arr), temp_value, arr)
|
1651
|
-
|
1652
|
-
# Sort each row
|
1653
|
-
sorted_arr = np.sort(arr_no_nan, axis=1)
|
1654
|
-
|
1655
|
-
# Move NaNs to the end
|
1656
|
-
result_arr = np.where(sorted_arr == temp_value, np.nan, sorted_arr)
|
1657
|
-
else:
|
1658
|
-
result_rows = []
|
1659
|
-
for row in arr:
|
1660
|
-
# Separate non-NaN and NaN values
|
1661
|
-
non_nan_values = row[~np.isnan(row)]
|
1662
|
-
nan_count = np.isnan(row).sum()
|
1663
|
-
# Create a new row with non-NaN values followed by NaNs
|
1664
|
-
new_row = np.concatenate([non_nan_values, [np.nan] * nan_count])
|
1665
|
-
result_rows.append(new_row)
|
1666
|
-
# Convert the list of rows back into a 2D NumPy array
|
1667
|
-
result_arr = np.array(result_rows)
|
1668
|
-
|
1669
|
-
# Remove rows/columns that contain only NaNs
|
1670
|
-
clean_arr = result_arr[~np.isnan(result_arr).all(axis=1)]
|
1671
|
-
clean_arr_ = clean_arr[:, ~np.isnan(clean_arr).all(axis=0)]
|
1672
|
-
|
1673
|
-
return clean_arr_
|
1674
|
-
|
1675
|
-
|
1676
|
-
def df2array(data: pd.DataFrame, x, y, hue=None, sort=False):
|
1677
|
-
if hue is None:
|
1678
|
-
a = []
|
1679
|
-
if sort:
|
1680
|
-
np.sort(data[x].unique().tolist()).tolist()
|
1681
|
-
else:
|
1682
|
-
cat_x = data[x].unique().tolist()
|
1683
|
-
for i, x_ in enumerate(cat_x):
|
1684
|
-
new_ = data.loc[data[x] == x_, y].to_list()
|
1685
|
-
a = padcat(a, new_, axis=0)
|
1686
|
-
return sort_rows_move_nan(a).T
|
1687
|
-
else:
|
1688
|
-
a = []
|
1689
|
-
if sort:
|
1690
|
-
cat_x = np.sort(data[x].unique().tolist()).tolist()
|
1691
|
-
cat_hue = np.sort(data[hue].unique().tolist()).tolist()
|
1692
|
-
else:
|
1693
|
-
cat_x = data[x].unique().tolist()
|
1694
|
-
cat_hue = data[hue].unique().tolist()
|
1695
|
-
for i, x_ in enumerate(cat_x):
|
1696
|
-
for j, hue_ in enumerate(cat_hue):
|
1697
|
-
new_ = data.loc[(data[x] == x_) & (data[hue] == hue_), y].to_list()
|
1698
|
-
a = padcat(a, new_, axis=0)
|
1699
|
-
return sort_rows_move_nan(a).T
|
1700
|
-
|
1701
|
-
|
1702
1579
|
def generate_xticks_with_gap(x_len, hue_len):
|
1703
1580
|
"""
|
1704
1581
|
Generate a concatenated array based on x_len and hue_len,
|
@@ -1728,3 +1605,18 @@ def generate_xticks_x_labels(x_len, hue_len):
|
|
1728
1605
|
for i in range(max(x_len, hue_len), 0, -1) # i iterates from 3 to 1
|
1729
1606
|
]
|
1730
1607
|
return [np.mean(i) for i in arrays if np.mean(i) > 0]
|
1608
|
+
|
1609
|
+
|
1610
|
+
def remove_colors_in_dict(
|
1611
|
+
data: dict, sections_to_remove_facecolor=["b", "e", "s", "bx", "v"]
|
1612
|
+
):
|
1613
|
+
# Remove "FaceColor" from specified sections
|
1614
|
+
for section in sections_to_remove_facecolor:
|
1615
|
+
if section in data and ("FaceColor" in data[section]):
|
1616
|
+
del data[section]["FaceColor"]
|
1617
|
+
|
1618
|
+
if "c" in data:
|
1619
|
+
del data["c"]
|
1620
|
+
if "loc" in data:
|
1621
|
+
del data["loc"]
|
1622
|
+
return data
|