tags:

views:

140

answers:

5

I have a list of numbers I am reading left to right. Anytime I encounter a sign change when reading the sequence I want to count it.

X = [-3,2,7,-4,1,-1,1,6,-1,0,-2,1] 
X = [-, +, +, -, +, -, +, +, -, -,-,+]

So, in this list there are 8 sign changes.

When Item [0] (in this case -3) is negative it is considered a sign change. Also, any 0 in the list is considered [-].

Any help would be greatly appreciated.

+9  A: 

You can use itertools.groupby to count the groups of positive and non-positive numbers:

>>> x = [-3,2,7,-4,1,-1,1,6,-1,0,-2,1] 

>>> import itertools
>>> len(list(itertools.groupby(x, lambda x: x > 0)))

Result:

8

In your question you state that you want:

  • to count the changes, not the groups
  • to count an extra change if the first element is not positive.

You can do this either by testing the first element directly and adjusting the result:

>>> len(list(itertools.groupby(x, lambda x: x > 0))) - (x[0] > 0)

or by prepending a positive number to the input before doing the grouping then subtracting 1 from the result:

>>> len(list(itertools.groupby(itertools.chain([1], x), lambda x: x > 0))) - 1

Watch out if your input list could by empty - the former solution will raise an exception.

Mark Byers
+1 because I wrote the same, except it looks like `len(list(itertools.groupby(x > 0 for x in X)))`
THC4k
`for x in X` seriously? That's worse than `for a in b`
Falmarri
+2  A: 
X = [-3,2,7,-4,1,-1,1,6,-1,0,-2,1]

last_sign = 1
sign_changes = 0

for x in X:
    if x == 0:
        sign = -1
    else:
        sign = x / abs(x)

    if sign == -last_sign:
        sign_changes = sign_changes + 1
        last_sign = sign

print sign_changes
Christian Jonassen
+1 Keep It Simple. No need to go all functional for this.
Joey Adams
How about `for x in X:` to simplify the loop?
Mark Byers
A: 
numbers = [-3,2,7,-4,1,-1,1,6,-1,0,-2,1]
# could be replaced by     signs = [x > 0 for x in numbers]
# but this methods gives us nice minus and plus signs
signs = map(lambda x: "+" if x > 0 else "-", numbers)

# zip(…) creates the pairs, each pair that has different signs
# adds one to "count"
count = sum(1 for x,y in zip(signs[:-1], signs[1:]) if x != y)

-> 7

For your additional requirement, that a negative number at the start of the list should be considered another change, just add a positive number to your list.

If you're dealing with huge lists, consider using generators. (izip, tee, …)

Georg
A: 

Here is a solution using fold, have fun figuring it out:

def lolwut((x,c), y):
    return (y, c+(x^y))

print reduce( lolwut ,(x > 0 for x in X), (True,0)) # 8
print reduce( lolwut ,(x > 0 for x in X), (False,0)) # 7
THC4k
A: 

If you haven't been convinced to read the itertools documentation yet:

def pairs(iterable):
    'iter -> (iter0, iter1), (iter1, iter2), (iter3, iter4), ...'
    from itertools import izip, tee
    first, second = tee(iterable)
    second.next()
    return izip(first, second)

def sign_changes(l):
    result = 0
    if l and l[0]<=0: result += 1
    result += sum(1 for a,b in pairs(l) if b*a<=0 and (a!=0 or b!=0))
    return result
Peter Milley