@@ -277,6 +277,7 @@ def __init__( # noqa: PLR0913
277277 )
278278 ),
279279 "_filter_range_sliders" : {},
280+ "_initializing" : True ,
280281 }
281282
282283 if self ._custom_attrs ["data_context_dir" ] is not None :
@@ -351,6 +352,7 @@ def __init__( # noqa: PLR0913
351352 # Wrap methods so they return CytoDataFrames
352353 # instead of Pandas DataFrames.
353354 self ._wrap_methods ()
355+ self ._custom_attrs ["_initializing" ] = False
354356
355357 def __getitem__ (self : CytoDataFrame_type , key : Union [int , str ]) -> Any :
356358 """
@@ -371,28 +373,50 @@ def __getitem__(self: CytoDataFrame_type, key: Union[int, str]) -> Any:
371373 return result
372374
373375 elif isinstance (result , pd .DataFrame ):
374- cdf = CytoDataFrame (
375- super ().__getitem__ (key ),
376- data_context_dir = self ._custom_attrs ["data_context_dir" ],
377- data_image_paths = self ._custom_attrs ["data_image_paths" ],
378- data_bounding_box = self ._custom_attrs ["data_bounding_box" ],
379- compartment_center_xy = self ._custom_attrs ["compartment_center_xy" ],
380- data_mask_context_dir = self ._custom_attrs ["data_mask_context_dir" ],
381- data_outline_context_dir = self ._custom_attrs ["data_outline_context_dir" ],
382- segmentation_file_regex = self ._custom_attrs ["segmentation_file_regex" ],
383- image_adjustment = self ._custom_attrs ["image_adjustment" ],
384- display_options = self ._custom_attrs ["display_options" ],
385- )
376+ return self ._build_result_cdf (result )
386377
387- # add widget control meta
388- cdf ._custom_attrs ["_widget_state" ] = self ._custom_attrs ["_widget_state" ]
389- cdf ._custom_attrs ["_scale_slider" ] = self ._custom_attrs ["_scale_slider" ]
390- cdf ._custom_attrs ["_filter_range_sliders" ] = self ._custom_attrs [
391- "_filter_range_sliders"
392- ]
393- cdf ._custom_attrs ["_output" ] = self ._custom_attrs ["_output" ]
378+ @property
379+ def _constructor (self : CytoDataFrame_type ) -> Callable [..., CytoDataFrame_type ]:
380+ """Return a constructor that preserves CytoDataFrame display state."""
381+ if self ._custom_attrs .get ("_initializing" , False ):
382+ return pd .DataFrame
383+ return self ._build_result_cdf
394384
395- return cdf
385+ def _build_result_cdf (
386+ self : CytoDataFrame_type ,
387+ data : Union ["CytoDataFrame" , pd .DataFrame , pd .Series , Any ],
388+ * args : Tuple [Any , ...],
389+ ** kwargs : Dict [str , Any ],
390+ ) -> CytoDataFrame_type :
391+ """Construct a result frame while preserving CytoDataFrame metadata."""
392+ if data is None and "data" in kwargs :
393+ data = kwargs .pop ("data" )
394+
395+ cdf = CytoDataFrame (
396+ data = data ,
397+ data_context_dir = self ._custom_attrs ["data_context_dir" ],
398+ data_image_paths = self ._custom_attrs ["data_image_paths" ],
399+ data_bounding_box = self ._custom_attrs ["data_bounding_box" ],
400+ compartment_center_xy = self ._custom_attrs ["compartment_center_xy" ],
401+ data_mask_context_dir = self ._custom_attrs ["data_mask_context_dir" ],
402+ data_outline_context_dir = self ._custom_attrs ["data_outline_context_dir" ],
403+ segmentation_file_regex = self ._custom_attrs ["segmentation_file_regex" ],
404+ image_adjustment = self ._custom_attrs ["image_adjustment" ],
405+ display_options = self ._custom_attrs ["display_options" ],
406+ * args ,
407+ ** kwargs ,
408+ )
409+
410+ # Preserve the shared interactive widget objects across derived views.
411+ cdf ._custom_attrs ["is_transposed" ] = self ._custom_attrs ["is_transposed" ]
412+ cdf ._custom_attrs ["_widget_state" ] = self ._custom_attrs ["_widget_state" ]
413+ cdf ._custom_attrs ["_scale_slider" ] = self ._custom_attrs ["_scale_slider" ]
414+ cdf ._custom_attrs ["_filter_range_sliders" ] = self ._custom_attrs [
415+ "_filter_range_sliders"
416+ ]
417+ cdf ._custom_attrs ["_output" ] = self ._custom_attrs ["_output" ]
418+
419+ return cdf
396420
397421 def _return_cytodataframe (
398422 self : CytoDataFrame_type ,
@@ -423,34 +447,19 @@ def _return_cytodataframe(
423447
424448 """
425449
426- result = method (* args , ** kwargs )
450+ result = (
451+ pd .DataFrame (self ).transpose (* args , ** kwargs )
452+ if method_name == "transpose"
453+ else method (* args , ** kwargs )
454+ )
427455
428456 if isinstance (result , pd .DataFrame ):
429- cdf = CytoDataFrame (
430- data = result ,
431- data_context_dir = self ._custom_attrs ["data_context_dir" ],
432- data_image_paths = self ._custom_attrs ["data_image_paths" ],
433- data_bounding_box = self ._custom_attrs ["data_bounding_box" ],
434- compartment_center_xy = self ._custom_attrs ["compartment_center_xy" ],
435- data_mask_context_dir = self ._custom_attrs ["data_mask_context_dir" ],
436- data_outline_context_dir = self ._custom_attrs ["data_outline_context_dir" ],
437- segmentation_file_regex = self ._custom_attrs ["segmentation_file_regex" ],
438- image_adjustment = self ._custom_attrs ["image_adjustment" ],
439- display_options = self ._custom_attrs ["display_options" ],
440- )
457+ cdf = self ._build_result_cdf (result )
441458 # If the method name is transpose we know that
442459 # the dataframe has been transposed.
443460 if method_name == "transpose" and not self ._custom_attrs ["is_transposed" ]:
444461 cdf ._custom_attrs ["is_transposed" ] = True
445462
446- # add widget control meta
447- cdf ._custom_attrs ["_widget_state" ] = self ._custom_attrs ["_widget_state" ]
448- cdf ._custom_attrs ["_scale_slider" ] = self ._custom_attrs ["_scale_slider" ]
449- cdf ._custom_attrs ["_filter_range_sliders" ] = self ._custom_attrs [
450- "_filter_range_sliders"
451- ]
452- cdf ._custom_attrs ["_output" ] = self ._custom_attrs ["_output" ]
453-
454463 return cdf
455464
456465 def _wrap_method (self : CytoDataFrame_type , method_name : str ) -> Callable :
@@ -4346,7 +4355,7 @@ def _generate_jupyter_dataframe_html( # noqa: C901, PLR0912, PLR0915
43464355 # if the data are transposed,
43474356 # we transpose them back to keep
43484357 # logic the same here.
4349- data = self .transpose ()
4358+ data = pd . DataFrame ( self ) .transpose ()
43504359
43514360 # Re-add bounding box columns if they are no longer available
43524361 bounding_box_externally_joined = False
@@ -4362,7 +4371,12 @@ def _generate_jupyter_dataframe_html( # noqa: C901, PLR0912, PLR0915
43624371 )
43634372 bounding_box_externally_joined = True
43644373 else :
4365- data = self .copy () if not bounding_box_externally_joined else data
4374+ data = (
4375+ data
4376+ if self ._custom_attrs ["is_transposed" ]
4377+ or bounding_box_externally_joined
4378+ else self .copy ()
4379+ )
43664380
43674381 # Re-add compartment center xy columns if they are no longer available
43684382 compartment_center_externally_joined = False
@@ -4381,7 +4395,8 @@ def _generate_jupyter_dataframe_html( # noqa: C901, PLR0912, PLR0915
43814395 else :
43824396 data = (
43834397 data
4384- if bounding_box_externally_joined
4398+ if self ._custom_attrs ["is_transposed" ]
4399+ or bounding_box_externally_joined
43854400 or compartment_center_externally_joined
43864401 else self .copy ()
43874402 )
@@ -4411,7 +4426,9 @@ def _generate_jupyter_dataframe_html( # noqa: C901, PLR0912, PLR0915
44114426 else :
44124427 data = (
44134428 data
4414- if image_paths_externally_joined or bounding_box_externally_joined
4429+ if self ._custom_attrs ["is_transposed" ]
4430+ or image_paths_externally_joined
4431+ or bounding_box_externally_joined
44154432 else self .copy ()
44164433 )
44174434
0 commit comments