@@ -2324,6 +2324,163 @@ def replace(self, axes_to_replace=None, new_axis=None, inplace=False, **kwargs):
2324
2324
else :
2325
2325
return AxisCollection (axes )
2326
2326
2327
+ def _guess_axis (self , axis_key ):
2328
+ if isinstance (axis_key , Group ):
2329
+ group_axis = axis_key .axis
2330
+ if group_axis is not None :
2331
+ # we have axis information but not necessarily an Axis object from self.axes
2332
+ real_axis = self [group_axis ]
2333
+ if group_axis is not real_axis :
2334
+ axis_key = axis_key .with_axis (real_axis )
2335
+ return axis_key
2336
+
2337
+ # TODO: instead of checking all axes, we should have a big mapping
2338
+ # (in AxisCollection or LArray):
2339
+ # label -> (axis, index)
2340
+ # or possibly (for ambiguous labels)
2341
+ # label -> {axis: index}
2342
+ # but for Pandas, this wouldn't work, we'd need label -> axis
2343
+ valid_axes = []
2344
+ for axis in self :
2345
+ try :
2346
+ axis .index (axis_key )
2347
+ valid_axes .append (axis )
2348
+ except KeyError :
2349
+ continue
2350
+ if not valid_axes :
2351
+ raise ValueError ("%s is not a valid label for any axis" % axis_key )
2352
+ elif len (valid_axes ) > 1 :
2353
+ valid_axes = ', ' .join (a .name if a .name is not None else '{{{}}}' .format (self .axes .index (a ))
2354
+ for a in valid_axes )
2355
+ raise ValueError ('%s is ambiguous (valid in %s)' % (axis_key , valid_axes ))
2356
+ return valid_axes [0 ][axis_key ]
2357
+
2358
+ def set_labels (self , axis = None , labels = None , inplace = False , ** kwargs ):
2359
+ r"""Replaces the labels of one or several axes.
2360
+
2361
+ Parameters
2362
+ ----------
2363
+ axis : string or Axis or dict
2364
+ Axis for which we want to replace labels, or mapping {axis: changes} where changes can either be the
2365
+ complete list of labels, a mapping {old_label: new_label} or a function to transform labels.
2366
+ If there is no ambiguity (two or more axes have the same labels), `axis` can be a direct mapping
2367
+ {old_label: new_label}.
2368
+ labels : int, str, iterable or mapping or function, optional
2369
+ Integer or list of values usable as the collection of labels for an Axis. If this is mapping, it must be
2370
+ {old_label: new_label}. If it is a function, it must be a function accepting a single argument (a
2371
+ label) and returning a single value. This argument must not be used if axis is a mapping.
2372
+ inplace : bool, optional
2373
+ Whether or not to modify the original object or return a new AxisCollection and leave the original intact.
2374
+ Defaults to False.
2375
+ **kwargs :
2376
+ `axis`=`labels` for each axis you want to set labels.
2377
+
2378
+ Returns
2379
+ -------
2380
+ AxisCollection
2381
+ AxisCollection with modified labels.
2382
+
2383
+ Examples
2384
+ --------
2385
+ >>> from larray import ndtest
2386
+ >>> axes = AxisCollection('nat=BE,FO;sex=M,F')
2387
+ >>> axes
2388
+ AxisCollection([
2389
+ Axis(['BE', 'FO'], 'nat'),
2390
+ Axis(['M', 'F'], 'sex')
2391
+ ])
2392
+ >>> axes.set_labels('sex', ['Men', 'Women'])
2393
+ AxisCollection([
2394
+ Axis(['BE', 'FO'], 'nat'),
2395
+ Axis(['Men', 'Women'], 'sex')
2396
+ ])
2397
+
2398
+ when passing a single string as labels, it will be interpreted to create the list of labels, so that one can
2399
+ use the same syntax than during axis creation.
2400
+
2401
+ >>> axes.set_labels('sex', 'Men,Women')
2402
+ AxisCollection([
2403
+ Axis(['BE', 'FO'], 'nat'),
2404
+ Axis(['Men', 'Women'], 'sex')
2405
+ ])
2406
+
2407
+ to replace only some labels, one must give a mapping giving the new label for each label to replace
2408
+
2409
+ >>> axes.set_labels('sex', {'M': 'Men'})
2410
+ AxisCollection([
2411
+ Axis(['BE', 'FO'], 'nat'),
2412
+ Axis(['Men', 'F'], 'sex')
2413
+ ])
2414
+
2415
+ to transform labels by a function, use any function accepting and returning a single argument:
2416
+
2417
+ >>> axes.set_labels('nat', str.lower)
2418
+ AxisCollection([
2419
+ Axis(['be', 'fo'], 'nat'),
2420
+ Axis(['M', 'F'], 'sex')
2421
+ ])
2422
+
2423
+ to replace labels for several axes at the same time, one should give a mapping giving the new labels for each
2424
+ changed axis
2425
+
2426
+ >>> axes.set_labels({'sex': 'Men,Women', 'nat': 'Belgian,Foreigner'})
2427
+ AxisCollection([
2428
+ Axis(['Belgian', 'Foreigner'], 'nat'),
2429
+ Axis(['Men', 'Women'], 'sex')
2430
+ ])
2431
+
2432
+ or use keyword arguments
2433
+
2434
+ >>> axes.set_labels(sex='Men,Women', nat='Belgian,Foreigner')
2435
+ AxisCollection([
2436
+ Axis(['Belgian', 'Foreigner'], 'nat'),
2437
+ Axis(['Men', 'Women'], 'sex')
2438
+ ])
2439
+
2440
+ one can also replace some labels in several axes by giving a mapping of mappings
2441
+
2442
+ >>> axes.set_labels({'sex': {'M': 'Men'}, 'nat': {'BE': 'Belgian'}})
2443
+ AxisCollection([
2444
+ Axis(['Belgian', 'FO'], 'nat'),
2445
+ Axis(['Men', 'F'], 'sex')
2446
+ ])
2447
+
2448
+ when there is no ambiguity (two or more axes have the same labels), it is possible to give a mapping
2449
+ between old and new labels
2450
+
2451
+ >>> axes.set_labels({'M': 'Men', 'BE': 'Belgian'})
2452
+ AxisCollection([
2453
+ Axis(['Belgian', 'FO'], 'nat'),
2454
+ Axis(['Men', 'F'], 'sex')
2455
+ ])
2456
+ """
2457
+ if axis is None :
2458
+ changes = {}
2459
+ elif isinstance (axis , dict ):
2460
+ changes = axis
2461
+ elif isinstance (axis , (basestring , Axis , int )):
2462
+ changes = {axis : labels }
2463
+ else :
2464
+ raise ValueError ("Expected None or a string/int/Axis/dict instance for axis argument" )
2465
+ changes .update (kwargs )
2466
+ # TODO: we should implement the non-dict behavior in Axis.replace, so that we can simplify this code to:
2467
+ # new_axes = [self[old_axis].replace(axis_changes) for old_axis, axis_changes in changes.items()]
2468
+ new_axes = []
2469
+ for old_axis , axis_changes in changes .items ():
2470
+ try :
2471
+ real_axis = self [old_axis ]
2472
+ except KeyError :
2473
+ axis_changes = {old_axis : axis_changes }
2474
+ real_axis = self ._guess_axis (old_axis ).axis
2475
+ if isinstance (axis_changes , dict ):
2476
+ new_axis = real_axis .replace (axis_changes )
2477
+ elif callable (axis_changes ):
2478
+ new_axis = real_axis .apply (axis_changes )
2479
+ else :
2480
+ new_axis = Axis (axis_changes , real_axis .name )
2481
+ new_axes .append ((real_axis , new_axis ))
2482
+ return self .replace (new_axes , inplace = inplace )
2483
+
2327
2484
# TODO: deprecate method (should use __sub__ instead)
2328
2485
def without (self , axes ):
2329
2486
"""
0 commit comments