@@ -2439,37 +2439,6 @@ def replace(self, axes_to_replace=None, new_axis=None, inplace=False, **kwargs)
2439
2439
else :
2440
2440
return AxisCollection (axes )
2441
2441
2442
- def _guess_axis (self , axis_key ):
2443
- if isinstance (axis_key , Group ):
2444
- group_axis = axis_key .axis
2445
- if group_axis is not None :
2446
- # we have axis information but not necessarily an Axis object from self.axes
2447
- real_axis = self [group_axis ]
2448
- if group_axis is not real_axis :
2449
- axis_key = axis_key .with_axis (real_axis )
2450
- return axis_key
2451
-
2452
- # TODO: instead of checking all axes, we should have a big mapping
2453
- # (in AxisCollection or Array):
2454
- # label -> (axis, index)
2455
- # or possibly (for ambiguous labels)
2456
- # label -> {axis: index}
2457
- # but for Pandas, this wouldn't work, we'd need label -> axis
2458
- valid_axes = []
2459
- for axis in self :
2460
- try :
2461
- axis .index (axis_key )
2462
- valid_axes .append (axis )
2463
- except KeyError :
2464
- continue
2465
- if not valid_axes :
2466
- raise ValueError (f"{ axis_key } is not a valid label for any axis\n { self .info } " )
2467
- elif len (valid_axes ) > 1 :
2468
- valid_axes = ', ' .join (a .name if a .name is not None else f'{{{ self .axes .index (a )} }}'
2469
- for a in valid_axes )
2470
- raise ValueError (f'{ axis_key } is ambiguous (valid in { valid_axes } )' )
2471
- return valid_axes [0 ][axis_key ]
2472
-
2473
2442
def set_labels (self , axis = None , labels = None , inplace = False , ** kwargs ) -> 'AxisCollection' :
2474
2443
r"""Replaces the labels of one or several axes.
2475
2444
@@ -2676,9 +2645,44 @@ def index_first_compatible(axis):
2676
2645
# -1 in to_remove are not a problem since enumerate starts at 0
2677
2646
return AxisCollection ([axis for i , axis in enumerate (self ) if i not in to_remove ])
2678
2647
2648
+ def _translate_nice_key (self , axis_key ):
2649
+ # TODO: instead of checking all axes, we should have a big mapping (in AxisCollection):
2650
+ # label -> (axis, index) but for sparse/multi-index, this would not work, we'd need label -> axis
2651
+ valid_axes = []
2652
+ # TODO: use axis_key dtype to only check compatible axes
2653
+ for axis in self :
2654
+ try :
2655
+ axis_pos_key = axis .index (axis_key )
2656
+ valid_axes .append (axis )
2657
+ except KeyError :
2658
+ continue
2659
+ if not valid_axes :
2660
+ raise ValueError (f"{ axis_key !r} is not a valid label for any axis:\n { self ._axes_summary ()} " )
2661
+ elif len (valid_axes ) > 1 :
2662
+ raise ValueError (f'{ axis_key !r} is ambiguous, it is valid in the following axes:\n '
2663
+ f'{ self ._axes_summary (valid_axes )} ' )
2664
+ real_axis = valid_axes [0 ]
2665
+ return real_axis , axis_pos_key
2666
+
2667
+ def _guess_axis (self , axis_key ):
2668
+ """
2669
+ Translates any *single axis* key to an LGroup on the real axis.
2670
+ """
2671
+ if isinstance (axis_key , Group ):
2672
+ group_axis = axis_key .axis
2673
+ if group_axis is not None :
2674
+ # we have axis information but not necessarily an Axis object from self
2675
+ real_axis = self [group_axis ]
2676
+ if group_axis is not real_axis :
2677
+ axis_key = axis_key .with_axis (real_axis )
2678
+ return axis_key
2679
+
2680
+ real_axis , axis_pos_key = self ._translate_nice_key (axis_key )
2681
+ return real_axis [axis_key ]
2682
+
2679
2683
def _translate_axis_key_chunk (self , axis_key ):
2680
2684
"""
2681
- Translates *single axis* label-based key to an IGroup
2685
+ Translates any *single axis* label-based key to an (axis, indices) pair.
2682
2686
2683
2687
Parameters
2684
2688
----------
@@ -2717,7 +2721,8 @@ def _translate_axis_key_chunk(self, axis_key):
2717
2721
try :
2718
2722
axis_pos_key = real_axis .index (axis_key )
2719
2723
except KeyError :
2720
- raise ValueError (f"{ axis_key !r} is not a valid label for any axis" )
2724
+ raise ValueError (f"{ axis_key !r} is not a valid label for the { real_axis .name !r} axis "
2725
+ f"with labels: { ', ' .join (repr (label ) for label in real_axis .labels )} " )
2721
2726
return real_axis , axis_pos_key
2722
2727
except KeyError :
2723
2728
# axis associated with axis_key may not belong to self.
@@ -2726,26 +2731,7 @@ def _translate_axis_key_chunk(self, axis_key):
2726
2731
axis_key = axis_key .to_label ()
2727
2732
2728
2733
# otherwise we need to guess the axis
2729
- # TODO: instead of checking all axes, we should have a big mapping (in AxisCollection):
2730
- # label -> (axis, index) but for sparse/multi-index, this would not work, we'd need label -> axis
2731
- valid_axes = []
2732
- # TODO: use axis_key dtype to only check compatible axes
2733
- for axis in self :
2734
- try :
2735
- axis_pos_key = axis .index (axis_key )
2736
- valid_axes .append (axis )
2737
- except KeyError :
2738
- continue
2739
- if not valid_axes :
2740
- raise ValueError (f"{ axis_key !r} is not a valid label for any axis" )
2741
- elif len (valid_axes ) > 1 :
2742
- # TODO: make an AxisCollection.display_name(axis) method out of this
2743
- # valid_axes = ', '.join(self.display_name(axis) for a in valid_axes)
2744
- valid_axes = ', ' .join (a .name if a .name is not None else f'{{{ self .index (a )} }}'
2745
- for a in valid_axes )
2746
- raise ValueError (f'{ axis_key } is ambiguous (valid in { valid_axes } )' )
2747
- real_axis = valid_axes [0 ]
2748
- return real_axis , axis_pos_key
2734
+ return self ._translate_nice_key (axis_key )
2749
2735
2750
2736
def _translate_axis_key (self , axis_key ):
2751
2737
"""
@@ -2878,7 +2864,7 @@ def _key_to_axis_indices_dict(self, key):
2878
2864
if has_duplicates (axis for axis , axis_key in key_items ):
2879
2865
dupe_axes = duplicates (axis for axis , axis_key in key_items )
2880
2866
dupe_axes_str = ', ' .join (str (axis ) for axis in dupe_axes )
2881
- raise ValueError (f"key has several values for axis: { dupe_axes_str } \n { key } " )
2867
+ raise ValueError (f"key has several values for axis: { dupe_axes_str } \n key: { key } " )
2882
2868
2883
2869
# ((axis, indices), (axis, indices), ...) -> dict
2884
2870
return dict (key_items )
@@ -3093,6 +3079,27 @@ def names(self) -> List[str]:
3093
3079
"""
3094
3080
return [axis .name for axis in self ._list ]
3095
3081
3082
+ # providing idx is just an optimization to avoid the relatively expensive self.index(axis)
3083
+ def _display_name (self , axis , idx = None ):
3084
+ if axis .name is None :
3085
+ if idx is None :
3086
+ idx = self .index (axis )
3087
+ name = f'{{{ idx } }}'
3088
+ else :
3089
+ # using str() because name can be an integer
3090
+ name = str (axis .name )
3091
+ return (name + '*' ) if axis .iswildcard else name
3092
+
3093
+ def _axes_summary (self , axes = None ):
3094
+ def axis_summary (axis , idx = None ):
3095
+ return f" { self ._display_name (axis , idx )} [{ len (axis )} ]: { axis .labels_summary ()} "
3096
+
3097
+ if axes is None :
3098
+ parts = [axis_summary (axis , i ) for i , axis in enumerate (self ._list )]
3099
+ else :
3100
+ parts = [axis_summary (axis ) for axis in axes ]
3101
+ return '\n ' .join (parts )
3102
+
3096
3103
@property
3097
3104
def display_names (self ) -> List [str ]:
3098
3105
r"""
@@ -3113,12 +3120,7 @@ def display_names(self) -> List[str]:
3113
3120
>>> AxisCollection([a, b, c, d]).display_names
3114
3121
['a', 'b*', '{2}', '{3}*']
3115
3122
"""
3116
- def display_name (i , axis ):
3117
- # str(axis.name) because name can be an integer
3118
- name = str (axis .name ) if axis .name is not None else f'{{{ i } }}'
3119
- return (name + '*' ) if axis .iswildcard else name
3120
-
3121
- return [display_name (i , axis ) for i , axis in enumerate (self ._list )]
3123
+ return [self ._display_name (axis , i ) for i , axis in enumerate (self ._list )]
3122
3124
3123
3125
@property
3124
3126
def ids (self ) -> List [Union [str , int ]]:
@@ -3233,10 +3235,8 @@ def info(self) -> str:
3233
3235
sex [2]: 'M' 'F'
3234
3236
time [4]: 2007 2008 2009 2010
3235
3237
"""
3236
- lines = [f" { name } [{ len (axis )} ]: { axis .labels_summary ()} "
3237
- for name , axis in zip (self .display_names , self ._list )]
3238
- shape = " x " .join (str (s ) for s in self .shape )
3239
- return ReprString ('\n ' .join ([shape ] + lines ))
3238
+ shape_str = " x " .join (str (s ) for s in self .shape )
3239
+ return ReprString (f"{ shape_str } \n { self ._axes_summary ()} " )
3240
3240
3241
3241
# XXX: instead of front_if_spread, we might want to require axes to be contiguous
3242
3242
# (ie the caller would have to transpose axes before calling this)
0 commit comments