2

Initial question

In Python, I would like to create a new variable c based on the value of a and b.

if a in ('GBP', 'AUD', 'CNY', 'NZD'):
    if b == '[00Y, 01Y]':
        c= '90'
    elif b == '[01Y, 02Y]':
        c = '85'
    elif b == '[02Y, 03Y]':
        c = '80'
    elif b == '[03Y, 04Y]':
        c = '75'
    elif b == '[04Y, 05Y]':
        c = '70'
elif a in ('EUR', 'USD', 'CHF', 'CAD', 'SGD', 'HKD', 'JPY'):
    if b == '[00Y, 01Y]':
        c = '95'
    elif b == '[01Y, 02Y]':
        c = '90'
    elif b == '[02Y, 03Y]':
        c = '85'
    elif b == '[03Y, 04Y]':
        c = '80'
    elif b == '[04Y, 05Y]':
        c = '75'
    elif b == '[05Y, 07Y]':
        c = '60'
    elif b == '[07Y, 10Y]':
        c = '55'

a and b are columns of a dataframe and I have to use apply to finally obtain what I desire.

Although this perfectly works, I think it is long code for such a small operation and I wonder if there is a more elegant way to do the same. I know the np.select condition but it forces me to repeat the condition on `a, which I find is not elegant either.

Thanks,

Reformulation of the question

My initial question is maybe not clear enough. I want to compact the following code without having to repeat all conditions:

def f1(a, b, c, d):
    if a == 1 and b <= 5 and c in ('abc', 'def') and d:         s = 75
    if a == 1 and b <= 5 and c in ('abc', 'def') and not d:     s = 83
    if a == 1 and b <= 5 and c == 'xyz' and d:                  s = 77
    if a == 1 and b <= 5 and c == 'xyz' and not d:              s = 17
    if a == 1 and 5 < b <= 8 and c in ('abc', 'def') and d:     s = 28
    if a == 1 and 5 < b <= 8 and c in ('abc', 'def') and not d: s = 39
    if a == 1 and 5 < b <= 8 and c == 'xyz' and d:              s = 10
    if a == 1 and 5 < b <= 8 and c == 'xyz' and not d:          s = 45
    if a == 1 and b > 8 and c in ('abc', 'def') and d:          s = 59
    if a == 1 and b > 8 and c in ('abc', 'def') and not d:      s = 48
    if a == 1 and b > 8 and c == 'xyz' and d:                   s = 29
    if a == 1 and b > 8 and c == 'xyz' and not d:               s = 24
    if a == 2 and b <= 5 and c in ('abc', 'def') and d:         s = 39
    if a == 2 and b <= 5 and c in ('abc', 'def') and not d:     s = 51
    if a == 2 and b <= 5 and c == 'xyz' and d:                  s = 69
    if a == 2 and b <= 5 and c == 'xyz' and not d:              s = 42
    if a == 2 and 5 < b <= 8 and c in ('abc', 'def') and d:     s = 23
    if a == 2 and 5 < b <= 8 and c in ('abc', 'def') and not d: s = 11
    if a == 2 and 5 < b <= 8 and c == 'xyz' and d:              s = 12
    if a == 2 and 5 < b <= 8 and c == 'xyz' and not d:          s = 89
    if a == 2 and b > 8 and c in ('abc', 'def') and d:          s = 54
    if a == 2 and b > 8 and c in ('abc', 'def') and not d:      s = 23
    if a == 2 and b > 8 and c == 'xyz' and d:                   s = 22
    if a == 2 and b > 8 and c == 'xyz' and not d:               s = 98
    if a == 3 and b <= 5 and c in ('abc', 'def') and d:         s = 91
    if a == 3 and b <= 5 and c in ('abc', 'def') and not d:     s = 15
    if a == 3 and b <= 5 and c == 'xyz' and d:                  s = 55
    if a == 3 and b <= 5 and c == 'xyz' and not d:              s = 36
    if a == 3 and 5 < b <= 8 and c in ('abc', 'def') and d:     s = 66
    if a == 3 and 5 < b <= 8 and c in ('abc', 'def') and not d: s = 82
    if a == 3 and 5 < b <= 8 and c == 'xyz' and d:              s = 20
    if a == 3 and 5 < b <= 8 and c == 'xyz' and not d:          s = 98
    if a == 3 and b > 8 and c in ('abc', 'def') and d:          s = 77
    if a == 3 and b > 8 and c in ('abc', 'def') and not d:      s = 23
    if a == 3 and b > 8 and c == 'xyz' and d:                   s = 41
    if a == 3 and b > 8 and c == 'xyz' and not d:               s = 84
    return s

Solution

I found this solution which uses itertools.product. but we need to pay attention to the order of listvalues:

import numpy as np
import itertools
def f(a, b, c, d):
    listconditions = [[a==1, a==2, a==3],
                      [b <= 5, 5 < b <= 8, b > 8],
                      [c in ("abc", "def"), c == 'xyz'],
                      [d, not d]]

        listvalues = [75, 83, 77, 17, 28, 39, 10, 45, 59, 48, 29, 24,
                      39, 51, 69, 42, 23, 11, 12, 89, 54, 23, 22, 98,
                      91, 15, 55, 36, 66, 82, 20, 98, 77, 23, 41, 84]

    allcombinations = itertools.product(*listconditions)

    test = [np.logical_and.reduce(i) for i in allcombinations]

    return sum(np.array(test) * listvalues)

f(1,7,'abc',False)

39

4
  • Could you please clarify if any of the answers fits your question, or they did not quite what you needed. Commented Oct 29, 2020 at 18:28
  • All answers fit my question. Thank you all Commented Oct 30, 2020 at 9:49
  • (ping) Could you please help stackoverflow mechanics by accepting the answer you liked the most (if there is one indeed) so that authors of answers don't see this question in their active list ;) thank you for participation. If none of answers was relevant pls ignore this ping. Commented Oct 30, 2020 at 11:45
  • @AndrewLyashko while it’s encouraged to accept answers, the OP is not required to Commented Nov 2, 2020 at 16:57

3 Answers 3

2

You could use a dictionary to contain indices for a and values in b:

options_a = {'GBP': 0, 'AUD': 0, 'CNY': 0, 'NZD': 0, 'EUR': 1, 'USD': 1, 'CHF': 1, 'CAD': 1, 'SGD': 1, 'HKD': 1, 'JPY': 1}

options_b = {'[00Y, 01Y]': ('90', '95'), '[01Y, 02Y]': ('85', '90'), '[02Y, 03Y]': ('80', '85'), '[03Y, 04Y]': ('75', '80'), '[04Y, 05Y]': ('70', '75'), '[05Y, 07Y]': (None, '60'), '[07Y, 10Y]': (None, '55')}

# Get the index of the tuple by looking up 'a' first
idx = options_a[a]

# Then use that index when you look up 'b' to grab the correct value for 'c'
c = options_b[b][idx]

If you get any combinations you didn't plan for, it will raise a KeyError, which you may or may not want to handle:

try:
    idx = options_a[a]
    tup = options_b[b]
except KeyError:
    print("Do something")
else:
    c = tup[idx]
Sign up to request clarification or add additional context in comments.

2 Comments

Is there a similar way when conditions are comparison?
@PierreYvesCorre could you provide an example of what you mean?
1

Use for loop and list to do what you want I assume that, you need to decrease the value by 5

tupel1 =  ('GBP', 'AUD', 'CNY', 'NZD')
tuple2 = ('EUR', 'USD', 'CHF', 'CAD', 'SGD', 'HKD', 'JPY')
listb = ['[00Y, 01Y]' ,'[02Y, 03Y]','[03Y, 04Y]','[04Y, 05Y]',]

for i in range(listb):
  if listb[i]==b:
    if a in tuble1:
       c = str(90 - 5*i)
    elif a in tuble2:
       c = str(95 -5*i)
  
 

2 Comments

do type casting
Small typo: tuple1 and tuple2 are defined initially, but tuble used in the code block.
0

Another take, a one-liner:

(95 if a in {'EUR', 'USD', 'CHF', 'CAD', 'SGD', 'HKD', 'JPY'} else 90) - 5 * [0, 1, 2, 3, 4, 5, 7].index(int(b[1:3]))

Or packing it more, but getting dirty:

(95 if a in 'EUR USD CHF CAD SGD HKD JPY' else 90) - 5 * [0, 1, 2, 3, 4, 5, 7].index(int(b[1:3]))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.