3

First post & I've probably got no business being here, but here goes...

How do I find the maximum and minimum values from the output of a 'for in' loop?

I've tried the min() and max() and get the following error...

TypeError: 'int' object is not iterable

here's my code...

import urllib2
import json

def printResults(data):
  # Use the json module to load the string data into a dictionary
  theJSON = json.loads(data)

  # test bed for accessing the data
  for i in theJSON["features"]:
   t = i["properties"]["time"]
   print t

def main():
  # define a variable to hold the source URL
  urlData = "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson"

  # Open the URL and read the data
  webUrl = urllib2.urlopen(urlData)
  #print webUrl.getcode()
  if (webUrl.getcode() == 200):
    data = webUrl.read()
    # print out our customized results
    printResults(data)
  else:
    print "Received an error from server, cannot retrieve results " +  str(webUrl.getcode())

if __name__ == "__main__":
   main()

Any pointers will be greatly appreciated!

1
  • 3
    please edit your post and include the full text of the error. It will tell you exactly where the problem is. Commented Mar 5, 2014 at 16:41

3 Answers 3

2

You can use min and max on iterables. Since you are looping through theJSON["features"], you can use:

print min(e["properties"]["time"] for e in theJSON["features"])
print max(e["properties"]["time"] for e in theJSON["features"])

You can also store the result in a variable, so you can use it later:

my_min = min(...)
my_max = max(...)

As @Sabyasachi commented you can also use:

print min(theJSON["features"], key = lambda x:x["properties"]["time"])
Sign up to request clarification or add additional context in comments.

5 Comments

Note that each of these will iterate the list again, so especially since you already iterate it yourself, you can just keep track of the minimum and maximum manually and save two extra iterations.
you can probably save time and space by using min(theJSON['features'],key=lambda x:x["properties"]["time"]) avoids the extra generator expression
@poke and sabyaschi: The OP did not mention performance being an issue, so clarity should be the biggest concern. If performance were the highest priority the OP probably wouldn't be using Python.
this works, but it prints out the values each time. How do I just pass it once to a variable that I can use later on? I then want to subtract the minimum from the maximum to get a 'start time value'.
@zedzero remember that this code will be outside a loop. You can use the variable assignment as I mentioned in the answer: some_variable = min(...)
1

Here is an example of how you can manually keep track of a min and max.

minVal = 0
maxVal = 0
for i in yourJsonThingy:
    if i < minVal:
        minVal = i
    if i > maxVal:
        maxVal = i

You can't do this:

for i in yourJsonThingy:
    maxVal = max(i)

Because i is just an integer and doesn't have a max

But you can perform those operations on a list of ints

maxVal = max(yourJsonThingy)
minVal = min(yourJsonThingy)

Comments

1

In the case that you only want to go through your iterable once, (say it's an expensive operation to do, and really that's the only reason you should do this, instead of doing max or min separately, but that said, the below is a performance improvement on calling both separately, see numbers below):

def max_min(iterable, key=None):
    ''' 
    returns a tuple of the max, min of iterable, optional function key 
    tuple items are None if iterable is of length 0
    '''
    it = iter(iterable)
    _max = _min = next(it, None)
    if key is None:
        for i in it:
            if i > _max:
                _max = i
            elif i < _min:
                _min = i
    else:
        _max_key = _min_key = key(_max)
        for i in it:
            key_i = key(i)
            if key_i > _max_key:
                _max, _max_key = i, key_i
            elif key_i < _min_key:
                _min, _min_key = i, key_i
    return _max, _min

usage:

>>> max_min(range(100))
(99, 0)
>>> max_min(range(100), key=lambda x: -x)
(0, 99)

To performance check:

>>> timeit.timeit('max(range(1000)), min(range(1000))', setup=setup)
70.95577674100059
>>> timeit.timeit('max_min(range(1000))', setup=setup)
65.00369232000958

Which is about a 9% improvement on calling both builtins, max and min, without a lambda, separately. With a lambda:

>>> timeit.timeit('max(range(1000), key=lambda x: -x),min(range(1000), key=lambda x: -x)', setup=setup)
294.17539755300095
>>> timeit.timeit('max_min(range(1000), key=lambda x: -x)', setup=setup)
208.95339999899443

Which is a more than 40% improvement on calling each separately with lambdas.

5 Comments

The test is kinda flawed, you shouldn't create range() twice when calling min/max separately. Also, when presenting performance improvement you need to use the original unoptimized speed as the divisor, so rather than 40%, your performance improvement is actually just under 30%.
Creating our iterable twice is the whole point of this code, we may not be able or want to materialize the entire dataset in memory. 30% less is (about) the same as 40% more, but thanks for pointing that out.
No, creating range twice isn't the point, it's flawed methodology, period. Creating the original iterable really should be passed to setup phase of timeit, otherwise what you're measuring is not the speed of max+min vs max_min but rather you're measuring two calls to range vs one call to range. I can't replicate your results, in no situation is the max_min is faster than two calls to max and min.
Also there are 33% difference between a 30% and 40% difference ((40%-30%)/30%=33%), which is not a negligible difference.
Think logarithmically.

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.