4
4
__all__ = ["setas" ]
5
5
import re
6
6
import copy
7
- from collections .abc import MutableMapping , Mapping
7
+ from collections .abc import MutableMapping , Mapping , Iterable
8
8
9
9
import numpy as np
10
10
@@ -137,15 +137,15 @@ def _prepare_call(self, args, kargs):
137
137
@property
138
138
def _size (self ):
139
139
"""Calculate a size of the setas attribute."""
140
- if len (self ._shape ) == 1 and self . _row :
141
- c = self ._shape [ 0 ]
142
- elif len ( self ._shape ) == 1 :
143
- c = 1
144
- elif len ( self . _shape ) > 1 :
145
- c = self . shape [ 1 ]
146
- else :
147
- c = len ( self . _column_headers )
148
- return c
140
+ match len (self .shape ) :
141
+ case 1 if self ._row :
142
+ return self ._shape [ 0 ]
143
+ case 1 :
144
+ return 1
145
+ case 0 :
146
+ return len ( self . _column_headers )
147
+ case _:
148
+ return self . shape [ 1 ]
149
149
150
150
@property
151
151
def _unique_headers (self ):
@@ -382,30 +382,32 @@ def __getitem__(self, name):
382
382
Either a single letter x,y,z,u,v,w,d,e or f, or a list of letters if used in
383
383
list mode, or a single coliumn name or list of names if used in dictionary mode.
384
384
"""
385
- if isinstance (name , string_types ) and len (name ) == 1 and name in "xyzuvwdef.-" :
386
- ret = self .to_dict ()[name ]
387
- if len (ret ) == 1 :
388
- ret = ret [0 ]
389
- elif isinstance (name , string_types ) and len (name ) == 2 and name [0 ] == "#" and name [1 ] in "xyzuvwdef.-" :
390
- ret = list ()
391
- name = name [1 ]
392
- s = 0
393
- while name in self ._setas [s :]:
394
- s = self ._setas .index (name ) + 1
395
- ret .append (s - 1 )
396
- if len (ret ) == 1 :
397
- ret = ret [0 ]
398
- elif isinstance (name , index_types ):
399
- ret = self .setas [self .find_col (name )]
400
- elif isinstance (name , slice ):
401
- indices = name .indices (len (self .setas ))
402
- name = range (* indices )
403
- ret = [self [x ] for x in name ]
404
- elif isiterable (name ):
405
- ret = [self [x ] for x in name ]
406
- else :
407
- raise IndexError (f"{ name } was not found in the setas attribute." )
408
- return ret
385
+ match name :
386
+ case "x" | "y" | "z" | "u" | "v" | "w" | "d" | "e" | "f" | "." | "-" :
387
+ ret = self .to_dict ()[name ]
388
+ if len (ret ) == 1 :
389
+ ret = ret [0 ]
390
+ return ret
391
+ case "#x" | "y#" | "#z" | "#u" | "#v" | "#w" | "#d" | "#e" | "#f" :
392
+ ret = list ()
393
+ name = name [1 ]
394
+ s = 0
395
+ while name in self ._setas [s :]:
396
+ s = self ._setas .index (name ) + 1
397
+ ret .append (s - 1 )
398
+ if len (ret ) == 1 :
399
+ ret = ret [0 ]
400
+ return ret
401
+ case int () | str () | _pattern_type ():
402
+ return self .setas [self .find_col (name )]
403
+ case slice ():
404
+ indices = name .indices (len (self .setas ))
405
+ name = range (* indices )
406
+ return [self [x ] for x in name ]
407
+ case _ if isiterable (name ):
408
+ return [self [x ] for x in name ]
409
+ case _:
410
+ raise IndexError (f"{ name } was not found in the setas attribute." )
409
411
410
412
def __iter__ (self ):
411
413
"""Iterate over thew column assignments.
@@ -432,26 +434,22 @@ def __setitem__(self, name, value):
432
434
a single letter string in the set above.
433
435
value (integer or column index): See above.
434
436
"""
435
- if isLikeList (name ): # Sipport indexing with a list like object
436
- if isLikeList (value ) and len (value ) == len (name ):
437
- for n , v in zip (name , value ):
438
- self ._setas [n ] = v
439
- else :
440
- for n in name :
441
- self [n ] = value
442
- elif isinstance (name , string_types ) and len (name ) == 1 and name in "xyzuvwdef.-" : # indexing by single letter
443
- for c in self .find_col (value , force_list = True ):
444
- self ._setas [c ] = name
445
- elif (
446
- isinstance (name , index_types )
447
- and isinstance (value , string_types )
448
- and len (value ) == 1
449
- and value in "xyzuvwdef.-"
450
- ):
451
- for c in self .find_col (name , force_list = True ):
452
- self .setas [c ] = value
453
- else :
454
- raise IndexError (f"Failed to set setas as couldn't workout what todo with setas[{ name } ] = { value } " )
437
+ match name :
438
+ case Iterable () if not isinstance (name , str ):
439
+ if isLikeList (value ) and len (value ) == len (name ):
440
+ for n , v in zip (name , value ):
441
+ self ._setas [n ] = v
442
+ else :
443
+ for n in name :
444
+ self [n ] = value
445
+ case "x" | "y" | "z" | "u" | "v" | "w" | "d" | "e" | "f" | "." | "-" :
446
+ for c in self .find_col (value , force_list = True ):
447
+ self ._setas [c ] = name
448
+ case int () | str () | _pattern_type () if value in [letter for letter in "xyzuvwdef.-" ]:
449
+ for c in self .find_col (name , force_list = True ):
450
+ self .setas [c ] = value
451
+ case _:
452
+ raise IndexError (f"Failed to set setas as couldn't workout what todo with setas[{ name } ] = { value } " )
455
453
456
454
def __len__ (self ):
457
455
"""Return our own length."""
@@ -503,48 +501,54 @@ def __iadd__(self, other):
503
501
504
502
def _sub_core_ (self , new , other ):
505
503
"""Implement subtracting either column indices or x,y,z,d,e,f,u,v,w for the current setas."""
506
- if isinstance (other , string_types ) and len (other ) == 1 and other in "xyzuvwdef" :
507
- while True :
508
- try :
509
- new ._setas [new ._setas .index (other )] = "."
510
- except ValueError :
511
- break
512
- return new
513
- if isinstance (other , index_types ):
504
+ if isinstance (other , str ):
514
505
try :
515
- new ._setas [new .find_col (other )] = "."
516
- return new
506
+ other = self .find_col (other )
517
507
except KeyError :
518
- other = new .clone (other , _self = True )
519
-
520
- if isinstance (other , Mapping ):
521
- me = new .to_dict ()
522
- other = new .clone (other , _self = True ).to_dict ()
523
- for k , v in other .items ():
524
- v = [v ] if not isinstance (v , list ) else v
525
- if k in me :
526
- for header in v :
527
- if header in me [k ]:
528
- if isinstance (me [k ], list ):
529
- me [k ].remove (header )
508
+ other = decode_string (other )
509
+ match other :
510
+ case "x" | "y" | "z" | "u" | "v" | "w" | "d" | "e" | "f" :
511
+ while other in new ._setas :
512
+ new ._setas [new ._setas .index (other )] = "."
513
+ return new
514
+ case dict ():
515
+ me = new .to_dict ()
516
+ other = new .clone (other , _self = True ).to_dict ()
517
+ for k , v in other .items ():
518
+ v = [v ] if not isinstance (v , list ) else v
519
+ if k in me :
520
+ for header in v :
521
+ if header in me [k ]:
522
+ if isinstance (me [k ], list ):
523
+ me [k ].remove (header )
524
+ else :
525
+ me [k ] = ""
526
+ if len (me [k ]) == 0 :
527
+ del me [k ]
530
528
else :
531
- me [k ] = ""
532
- else :
533
- raise ValueError (f"{ header } is not set as { k } " )
534
- if len (me [k ]) == 0 :
535
- del me [k ]
536
- else :
537
- raise ValueError (f"No column is set as { k } " )
538
- new .clear ()
539
- new (me )
540
- return new
541
- if isiterable (other ):
542
- for o in other :
543
- new = self ._sub_core_ (new , o )
544
- if new is NotImplemented :
545
- return NotImplemented
546
- return new
547
- return NotImplemented
529
+ raise ValueError (f"{ header } is not set as { k } " )
530
+ else :
531
+ raise ValueError (f"No column is set as { k } " )
532
+ new .clear ()
533
+ new (me )
534
+ return new
535
+ case Iterable () if all (isinstance (x , (str , int , slice , list , re .Pattern )) for x in other ):
536
+ for o in other :
537
+ if o == other :
538
+ continue
539
+ new = self ._sub_core_ (new , o )
540
+ if new is NotImplemented :
541
+ return NotImplemented
542
+ return new
543
+ case int () | slice () | list () | re .Pattern ():
544
+ try :
545
+ new ._setas [new .find_col (other )] = "."
546
+ return new
547
+ except KeyError :
548
+ other = new .clone (other , _self = True )
549
+
550
+ case _:
551
+ return NotImplemented
548
552
549
553
def __sub__ (self , other ):
550
554
"""Jump to the core."""
@@ -553,8 +557,7 @@ def __sub__(self, other):
553
557
554
558
def __isub__ (self , other ):
555
559
"""Jump to the core."""
556
- new = self
557
- return self ._sub_core_ (new , other )
560
+ return self ._sub_core_ (self , other )
558
561
559
562
def find_col (self , col , force_list = False ):
560
563
"""Indexes the column headers in order to locate a column of data.shape.
@@ -581,43 +584,44 @@ def find_col(self, col, force_list=False):
581
584
Returns:
582
585
The matching column index as an integer or a KeyError
583
586
"""
584
- if isinstance (col , int_types ): # col is an int so pass on
585
- if col >= len (self .column_headers ):
586
- raise IndexError (f"Attempting to index a non - existent column { col } " )
587
- if col < 0 :
588
- col = col % len (self .column_headers )
589
- elif isinstance (col , string_types ): # Ok we have a string
590
- col = str (col )
591
- if col in self .column_headers : # and it is an exact string match
592
- col = self .column_headers .index (col )
593
- else : # ok we'll try for a regular expression
594
- test = re .compile (col )
587
+ match col :
588
+ case int ():
589
+ if col >= len (self .column_headers ):
590
+ raise IndexError (f"Attempting to index a non - existent column { col } " )
591
+ if col < 0 :
592
+ col = col % len (self .column_headers )
593
+ case str ():
594
+ col = str (col )
595
+ if col in self .column_headers : # and it is an exact string match
596
+ col = self .column_headers .index (col )
597
+ else : # ok we'll try for a regular expression
598
+ test = re .compile (col )
599
+ possible = [x for x in self .column_headers if test .search (x )]
600
+ if not possible :
601
+ try :
602
+ col = int (col )
603
+ except ValueError as err :
604
+ raise KeyError (
605
+ f'Unable to find any possible column matches for "{ col } in { self .column_headers } "'
606
+ ) from err
607
+ if col < 0 or col >= self .data .shape [1 ]:
608
+ raise KeyError ("Column index out of range" )
609
+ else :
610
+ col = self .column_headers .index (possible [0 ])
611
+ case _pattern_type ():
612
+ test = col
595
613
possible = [x for x in self .column_headers if test .search (x )]
596
614
if not possible :
597
- try :
598
- col = int (col )
599
- except ValueError as err :
600
- raise KeyError (
601
- f'Unable to find any possible column matches for "{ col } in { self .column_headers } "'
602
- ) from err
603
- if col < 0 or col >= self .data .shape [1 ]:
604
- raise KeyError ("Column index out of range" )
605
- else :
606
- col = self .column_headers .index (possible [0 ])
607
- elif isinstance (col , _pattern_type ):
608
- test = col
609
- possible = [x for x in self .column_headers if test .search (x )]
610
- if not possible :
611
- raise KeyError (f"Unable to find any possible column matches for { col .pattern } " )
612
- col = self .find_col (possible )
613
- elif isinstance (col , slice ):
614
- indices = col .indices (self .shape [1 ])
615
- col = range (* indices )
616
- col = self .find_col (col )
617
- elif isiterable (col ):
618
- col = [self .find_col (x ) for x in col ]
619
- else :
620
- raise TypeError (f"Column index must be an integer, string, list or slice, not a { type (col )} " )
615
+ raise KeyError (f"Unable to find any possible column matches for { col .pattern } " )
616
+ col = self .find_col (possible )
617
+ case slice ():
618
+ indices = col .indices (self .shape [1 ])
619
+ col = range (* indices )
620
+ col = self .find_col (col )
621
+ case _ if isiterable (col ):
622
+ col = [self .find_col (x ) for x in col ]
623
+ case _:
624
+ raise TypeError (f"Column index must be an integer, string, list or slice, not a { type (col )} " )
621
625
if force_list and not isinstance (col , list ):
622
626
col = [col ]
623
627
return col
0 commit comments