2
\$\begingroup\$

Consider (simplified)

low_count = 0
high_count = 0
high = 10
low = 5
value = 2

What is a clean way to check a number value versus a min low and a max high, such that if value is below low low_count increments, and if value is above high high_count increments? Currently I have (code snippet)

            high_count = 0
            low_count = 0
            low = spec_df['Lower'][i]
            high = spec_df['Upper'][i]
            #Calculate results above/below limit
            for result in site_results:
                if result<low:
                    low_count+=1
                else:
                    if result>high:
                        high_count+=1

Thanks!

\$\endgroup\$
1
  • 4
    \$\begingroup\$ Use elif instead of else:\nif. \$\endgroup\$
    – Ry-
    Jul 16, 2013 at 0:39

5 Answers 5

3
\$\begingroup\$

I would write it as

low_count = sum(map(lambda x: x < low, site_results))
high_count = sum(map(lambda x: x > high, site_results))

but admit I'm spoiled by Haskell.

Edit: As suggested by @Nick Burns, using list comprehension will make it even clearer:

low_count = sum(x < low for x in site_results)
high_count = sum(x > high for x in site_results)
\$\endgroup\$
5
  • 1
    \$\begingroup\$ +1 - I agree that this is far more pythonic in this example. However, would probably use a simple generator inside sum: sum(x for x in site_results if x < low) \$\endgroup\$
    – Nick Burns
    Jul 16, 2013 at 3:54
  • 1
    \$\begingroup\$ I agree that the list comprehension is even clearer, but I believe you meant len, not sum (since the count of low-value elements is needed); it will also require wrapping the comprehension in list, since you can't take len of a generator. Alternatively, you can write sum(x < low for x in site_results) \$\endgroup\$
    – fjarri
    Jul 16, 2013 at 4:19
  • \$\begingroup\$ sorry -good point. This was a typo, meant to read: sum(1 for x in site_results if x < low) . I which case, no need for a list :) \$\endgroup\$
    – Nick Burns
    Jul 16, 2013 at 4:36
  • \$\begingroup\$ wouldn't this code be poor performing once you get a huge number of items in site_results? I'm thinking this is four linear, a.k.a. O(n), operations, since sum and map are O(n)...is this incorrect? \$\endgroup\$
    – user26977
    Jul 16, 2013 at 17:28
  • 2
    \$\begingroup\$ @omouse: not four, but only two, since map or list comprehensions return iterators, not lists. Yes, in my solution technically there will be more additions (you will add False values too), but @Nick's solution does not have this drawback. Finally, it's still O(N), same as the original code, and in the very unlikely event the different coefficient matters, it will be picked up during profiling. I prefer to optimize for humans first. \$\endgroup\$
    – fjarri
    Jul 17, 2013 at 0:13
2
\$\begingroup\$

You can make use of the fact that Python can interpret boolean values as integers:

for result in site_results:
    low_count  += result < low
    high_count += result > high

If those conditions evaluate to True, this will add 1 to the counts, otherwise 0. This is the same principle as used in the list comprehensions by Bogdan and Nick Burns:

high_count = sum(result > high for result in site_results)
\$\endgroup\$
1
\$\begingroup\$

Seems pretty clean. I would edit the following:

elif result>high:
   high_count+=1

Source: http://docs.python.org/2/tutorial/controlflow.html
Is the code not working for you or are you just looking for a better way?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ There is no elseif in Python. It's elif. \$\endgroup\$
    – abarnert
    Jul 16, 2013 at 0:52
  • \$\begingroup\$ Right you are, good eye! Edited. \$\endgroup\$
    – Cosco Tech
    Jul 16, 2013 at 1:02
0
\$\begingroup\$

The pythonic way would be:

 for result in site_results:
     if result<low:
        low_count+=1
     elif result>high:
        high_count+=1
\$\endgroup\$
1
  • 5
    \$\begingroup\$ Truly pythonic code would follow PEP8, particularly with regards to spacing \$\endgroup\$
    – ernie
    Jul 16, 2013 at 0:45
0
\$\begingroup\$

For a more complex case, I'd write a function and use a Counter. For example:

def categorize(low, high):
    def func(i):
        if i < low:
            return 'low'
        elif i > high:
            return 'high'
        else:
            return 'middle'
    return func

site_results = list(range(20))

counts = collections.Counter(map(categorize(5, 10), site_results))
print(counts['high'], counts['low'])

But for a trivial case like this, that would be silly. Other than tenstar's/minitech's suggestions, I wouldn't do anything different.

\$\endgroup\$
2
  • \$\begingroup\$ Wow - I was playing with: print Counter({-1: 'low', 1: 'high'}.get(cmp(result, low), 'eq') for result in site_results) \$\endgroup\$ Jul 16, 2013 at 0:53
  • \$\begingroup\$ @JonClements: If you want to one-liner-ize it, you'd need something like (cmp(result, low) + cmp(result, high)) // 2, which I think goes way beyond the bounds of readability… \$\endgroup\$
    – abarnert
    Jul 16, 2013 at 1:00

Your Answer

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