@@ -652,9 +652,6 @@ def set_cartesian_axis_opts(args, axis, letter, orders):
652
652
653
653
654
654
def configure_cartesian_marginal_axes (args , fig , orders ):
655
- if "histogram" in [args ["marginal_x" ], args ["marginal_y" ]]:
656
- fig .layout ["barmode" ] = "overlay"
657
-
658
655
nrows = len (fig ._grid_ref )
659
656
ncols = len (fig ._grid_ref [0 ])
660
657
@@ -1489,25 +1486,21 @@ def build_dataframe(args, constructor):
1489
1486
# PySpark to pandas.
1490
1487
is_pd_like = False
1491
1488
1492
- # Flag that indicates if data_frame requires to be converted to arrow via the
1493
- # dataframe interchange protocol.
1494
- # True if Ibis, DuckDB, Vaex or implements __dataframe__
1489
+ # Flag that indicates if data_frame needs to be converted to PyArrow.
1490
+ # True if Ibis, DuckDB, Vaex, or implements __dataframe__
1495
1491
needs_interchanging = False
1496
1492
1497
1493
# If data_frame is provided, we parse it into a narwhals DataFrame, while accounting
1498
1494
# for compatibility with pandas specific paths (e.g. Index/MultiIndex case).
1499
1495
if df_provided :
1500
-
1501
1496
# data_frame is pandas-like DataFrame (pandas, modin.pandas, cudf)
1502
1497
if nw .dependencies .is_pandas_like_dataframe (args ["data_frame" ]):
1503
-
1504
1498
columns = args ["data_frame" ].columns # This can be multi index
1505
1499
args ["data_frame" ] = nw .from_native (args ["data_frame" ], eager_only = True )
1506
1500
is_pd_like = True
1507
1501
1508
1502
# data_frame is pandas-like Series (pandas, modin.pandas, cudf)
1509
1503
elif nw .dependencies .is_pandas_like_series (args ["data_frame" ]):
1510
-
1511
1504
args ["data_frame" ] = nw .from_native (
1512
1505
args ["data_frame" ], series_only = True
1513
1506
).to_frame ()
@@ -1993,7 +1986,6 @@ def process_dataframe_hierarchy(args):
1993
1986
1994
1987
if args ["color" ]:
1995
1988
if discrete_color :
1996
-
1997
1989
discrete_aggs .append (args ["color" ])
1998
1990
agg_f [args ["color" ]] = nw .col (args ["color" ]).max ()
1999
1991
agg_f [f'{ args ["color" ]} { n_unique_token } ' ] = (
@@ -2048,7 +2040,6 @@ def post_agg(dframe: nw.LazyFrame, continuous_aggs, discrete_aggs) -> nw.LazyFra
2048
2040
).drop ([f"{ col } { n_unique_token } " for col in discrete_aggs ])
2049
2041
2050
2042
for i , level in enumerate (path ):
2051
-
2052
2043
dfg = (
2053
2044
df .group_by (path [i :], drop_null_keys = True )
2054
2045
.agg (** agg_f )
@@ -2425,7 +2416,6 @@ def get_groups_and_orders(args, grouper):
2425
2416
# figure out orders and what the single group name would be if there were one
2426
2417
single_group_name = []
2427
2418
unique_cache = dict ()
2428
- grp_to_idx = dict ()
2429
2419
2430
2420
for i , col in enumerate (grouper ):
2431
2421
if col == one_group :
@@ -2443,27 +2433,28 @@ def get_groups_and_orders(args, grouper):
2443
2433
else :
2444
2434
orders [col ] = list (OrderedDict .fromkeys (list (orders [col ]) + uniques ))
2445
2435
2446
- grp_to_idx = {k : i for i , k in enumerate (orders )}
2447
-
2448
2436
if len (single_group_name ) == len (grouper ):
2449
2437
# we have a single group, so we can skip all group-by operations!
2450
2438
groups = {tuple (single_group_name ): df }
2451
2439
else :
2452
- required_grouper = list ( orders . keys ())
2440
+ required_grouper = [ group for group in orders if group in grouper ]
2453
2441
grouped = dict (df .group_by (required_grouper , drop_null_keys = True ).__iter__ ())
2454
- sorted_group_names = list (grouped .keys ())
2455
2442
2456
- for i , col in reversed (list (enumerate (required_grouper ))):
2457
- sorted_group_names = sorted (
2458
- sorted_group_names ,
2459
- key = lambda g : orders [col ].index (g [i ]) if g [i ] in orders [col ] else - 1 ,
2460
- )
2443
+ sorted_group_names = sorted (
2444
+ grouped .keys (),
2445
+ key = lambda values : [
2446
+ orders [group ].index (value ) if value in orders [group ] else - 1
2447
+ for group , value in zip (required_grouper , values )
2448
+ ],
2449
+ )
2461
2450
2462
2451
# calculate the full group_names by inserting "" in the tuple index for one_group groups
2463
2452
full_sorted_group_names = [
2464
2453
tuple (
2465
2454
[
2466
- "" if col == one_group else sub_group_names [grp_to_idx [col ]]
2455
+ ""
2456
+ if col == one_group
2457
+ else sub_group_names [required_grouper .index (col )]
2467
2458
for col in grouper
2468
2459
]
2469
2460
)
@@ -2490,6 +2481,10 @@ def make_figure(args, constructor, trace_patch=None, layout_patch=None):
2490
2481
constructor = go .Bar
2491
2482
args = process_dataframe_timeline (args )
2492
2483
2484
+ # If we have marginal histograms, set barmode to "overlay"
2485
+ if "histogram" in [args .get ("marginal_x" ), args .get ("marginal_y" )]:
2486
+ layout_patch ["barmode" ] = "overlay"
2487
+
2493
2488
trace_specs , grouped_mappings , sizeref , show_colorbar = infer_config (
2494
2489
args , constructor , trace_patch , layout_patch
2495
2490
)
@@ -2561,7 +2556,12 @@ def make_figure(args, constructor, trace_patch=None, layout_patch=None):
2561
2556
legendgroup = trace_name ,
2562
2557
showlegend = (trace_name != "" and trace_name not in trace_names ),
2563
2558
)
2564
- if trace_spec .constructor in [go .Bar , go .Violin , go .Box , go .Histogram ]:
2559
+
2560
+ # Set 'offsetgroup' only in group barmode (or if no barmode is set)
2561
+ barmode = layout_patch .get ("barmode" )
2562
+ if trace_spec .constructor in [go .Bar , go .Box , go .Violin , go .Histogram ] and (
2563
+ barmode == "group" or barmode is None
2564
+ ):
2565
2565
trace .update (alignmentgroup = True , offsetgroup = trace_name )
2566
2566
trace_names .add (trace_name )
2567
2567
0 commit comments