Reputation: 12747
I've already checked the following answers: Replacements for switch statement in Python? and How to refactor Python "switch statement"
but I think both refer to simpler switch statements with single cases.
I've got a problem where a switch statement would look something like this:
switch(view) {
case "negatives":
label = 0;
break;
case "cars\\00inclination_000azimuth":
case "buses\\00inclination_000azimuth":
case "trucks\\00inclination_000azimuth":
label = 1;
break;
case "cars\\00inclination_045azimuth":
case "buses\\00inclination_045azimuth":
case "trucks\\00inclination_045azimuth":
case "cars\\00inclination_090azimuth":
case "buses\\00inclination_090azimuth":
case "trucks\\00inclination_090zimuth":
case "cars\\00inclination_135azimuth":
case "buses\\00inclination_135azimuth":
case "trucks\\00inclination_135azimuth":
label = 2;
break;
# and so on
So there are many cases that result in the same label. Is there a quick way to do this using lists? Where I could use something like this
a = ["cars\\00inclination_045azimuth","buses\\00inclination_045azimuth","trucks\\00inclination_045azimuth","cars\\00inclination_090azimuth","buses\\00inclination_090azimuth", "trucks\\00inclination_090zimuth","cars\\00inclination_135azimuth","buses\\00inclination_135azimuth","trucks\\00inclination_135azimuth"]
if view in a:
label = 2
But then I'd have to make a list for every set of cases that map to the same label and then go through each of them.
Is there a way to do the following, and if not, then what is the easiest way to do this?
if view in _any_of_the_lists_i've_made:
label = the_index_of_that_list
The values I showed here in the question were just a few, in order to get a general idea of the problem. But I realized from some of the comments that it would be better to give the full range of values I have as cases.
So in total I have 25 views for each vehicle, and with 3 vehicles, that's 75 different possible views, i.e. 75 cases.
Upvotes: 1
Views: 144
Reputation: 103754
Since there is tremendous overlap in the mapping of vehicle:inclination:azimuth values, you can use intertools product to describe tuples of the overlapping strings to a mapping.
Suppose that you can describe label
as '2'
for all cases where vehicle is any of ['cars', 'buses', 'trucks']
, azimuth is any of ['0']
and inclination is any of ['45', '90', '135']
-- what your example says.
You can do:
from itertools import product
vehicles=['cars', 'buses', 'trucks']
switch={}
switch.update({t:2 for t in product(vehicles, ['0'], ['45', '90', '135'])})
print switch
# {('buses', '0', '90'): 2, ('buses', '0', '45'): 2, ('cars', '0', '135'): 2, ('trucks', '0', '135'): 2, ('trucks', '0', '90'): 2, ('cars', '0', '45'): 2, ('trucks', '0', '45'): 2, ('cars', '0', '90'): 2, ('buses', '0', '135'): 2}
Then it is easy to add addition 'case' values (to borrow the C sense of that) with .update, product and a dict comprehension:
>>> switch.update({t:1 for t in product(vehicles, ['0'], ['0'])})
>>> switch
{('buses', '0', '90'): 2, ('buses', '0', '45'): 2, ('cars', '0', '135'): 2, ('trucks', '0', '135'): 2, ('trucks', '0', '90'): 2, ('cars', '0', '45'): 2, ('trucks', '0', '45'): 2, ('cars', '0', '90'): 2, ('buses', '0', '135'): 2, ('cars', '0', '0'): 1, ('buses', '0', '0'): 1, ('trucks', '0', '0'): 1}
Repeat with each describe-able bucket of combinations of vehicle:inclination:azimuth values.
Then you can assign label directly from the key value of a tuple of the values of interest:
>>> label=switch[('cars', '0', '0')]
>>> label
1
Without having to type them all out.
You can also use .get()
to have a default value:
>>> switch.get(('bicycle','0','0'), 'default')
'default'
Upvotes: 0
Reputation: 1121494
Your view strings follow a pattern; they consist of <transport>\<inclination>_<azimuth>
, and your labels only really vary on the azimuth value.
Reduce the string to just the azimuth value, then use a dict
to find your label:
labels = {'000': 1, '045': 2, '090': 2, '135': 2}
label = 0 if view == 'negatives' else labels[view.rpartition('_')[-1][:3]]
This takes out just the 3 digits from the azimuth
string.
To illustrate how this works, a short demo:
>>> "cars\\00inclination_000azimuth".rpartition('_')
('cars\\00inclination', '_', '000azimuth')
>>> "cars\\00inclination_000azimuth".rpartition('_')[-1]
'000azimuth'
>>> "cars\\00inclination_000azimuth".rpartition('_')[-1][:3]
'000'
str.rpartition()
splits on the last _
character, from which we select the last element, and from that last element we slice the first 3 characters.
If you need to vary by inclination too, parse that out separely:
labels = {
None: 0,
('00', '000'): 1,
('00', '045'): 2,
('00', '090'): 2,
('00', '135'): 2,
# etc.
}
if view == 'negatives':
key = None
else:
key = view.partition('\\')[-1][:2], view.rpartition('_')[-1][:3]
label = labels[key]
By reducing the view to just those parts that matter you can greatly reduce the size of the dictionary, or at least that part you need to type out.
Of course, just looking up the view string directly would be faster. You can always generate the full strings from the above dictionary:
for key, label in labels.items():
if key is None:
labels['negatives'] = label
continue
for vehicle in ('cars', 'trucks', 'buses'):
labels['{}\\{}inclination_{}azimuth'.format(vehicle, *key)] = label
then look up your label directly:
label = labels[view]
Upvotes: 3
Reputation: 77337
Other answers here are good, but there's nothing wrong with a dict based lookup table - if you plan to access it often, a bigger dict is faster than preprocessing the string to extract values on each lookup.
def get_label(val):
label_map = {
"negatives": 0,
"cars\\00inclination_000azimuth": 1,
"buses\\00inclination_000azimuth": 1,
"trucks\\00inclination_000azimuth": 1,
"cars\\00inclination_045azimuth": 2,
(...etc...)
}
return label_map[val]
Upvotes: 0
Reputation: 375504
As some commenters have pointed out, your best solution is to parse the strings more fully, and get at the key information inside them, which will reduce your cases. If for some reason you cannot, you can do this:
CASES = [
{
"negatives",
},
{
"cars\\00inclination_000azimuth",
"buses\\00inclination_000azimuth",
"trucks\\00inclination_000azimuth",
},
{
"cars\\00inclination_045azimuth",
"buses\\00inclination_045azimuth",
"trucks\\00inclination_045azimuth",
"cars\\00inclination_090azimuth",
"buses\\00inclination_090azimuth",
"trucks\\00inclination_090zimuth",
"cars\\00inclination_135azimuth",
"buses\\00inclination_135azimuth",
"trucks\\00inclination_135azimuth",
},
]
def find_label(view):
for label, views in enumerate(CASES):
if view in views:
return label
Note that I've used sets to speed the view in views
check.
Even better, you can pre-process your list of sets into a single dictionary:
CASE_DICT = { view:label for label, views in enumerate(CASES) for view in views }
def find_label(view):
return CASE_DICT.get(view, None)
Upvotes: 0