2010-05-29 2 views
4

У меня есть список чисел, которые я читаю слева направо. Каждый раз, когда я сталкиваюсь с изменением знака при чтении последовательности, я хочу сосчитать ее.Python - изменения счетного знака

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

Итак, в этом списке 8 изменений знака.

Когда товар [0] (в данном случае -3) отрицательный, считается заменой знака. Кроме того, любое 0 в списке считается [-].

Любая помощь была бы принята с благодарностью.

+0

Если это помогает любому, отвечающему, вот решение Haskell: 'signChanges xs = sum $ zipWith (\ xy -> if (x> 0) == (y> 0), то 0 else 1) (1: xs) xs ' –

+0

Вот ссылка для тех, кто ищет [** решение, в котором нуль обрабатывается без изменений **] (http://stackoverflow.com/a/40804510/2192488). –

ответ

12

Вы можете использовать itertools.groupby для подсчета групп положительных и неположительными чисел:

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

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

Результат:

8 

В своем вопросе вы заявляете, что вы хотите:

  • для подсчета голосов, а не групп
  • , чтобы подсчитать дополнительное изменение, если первый элемент не является положительным.

Вы можете сделать это либо путем тестирования первого элемента непосредственно и доведением результата:

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

или предваряя положительное число на входе перед выполнением группировки затем вычитанием 1 из результата:

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

Остерегайтесь, если ваш входной список может быть пустым - первое решение вызовет исключение.

+2

+1, потому что я написал то же самое, за исключением того, что он выглядит как len (list (itertools.groupby (x> 0 для x в X))) ' –

+0

' for x in X' серьезно? Это хуже, чем 'for a in b' – Falmarri

3
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 
+0

+1 Keep It Simple. Не нужно все функционировать для этого. –

0
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

Для вашего дополнительного требования, что отрицательное число в начале списка следует рассматривать еще одно изменение, просто добавить положительное число в свой список.

Если вы имеете дело с огромными списками, подумайте об использовании генераторов. (Izip, тройник, ...)

0

Вот решение с использованием свёртки, получать удовольствие, выясняя его:

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 
0

Если вы не были убеждены, читать документацию itertools еще:

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 
0

Для целых чисел, (a^b) < 0, если знаки a и b различны.

def countSignChanges(seq): 
    # make sure 0's are treated as negative 
    seq = [-1 if not x else x for x in seq] 

    # zip with leading 1, so that opening negative value is 
    # treated as sign change 
    return sum((a^b)<0 for a,b in zip([1]+seq, seq)) 


X = [-3,2,7,-4,1,-1,1,6,-1,0,-2,1] 
print countSignChanges(X) 

дает желаемый ответ, .

0

Это способ сделать это без циклов ... Это должно быть намного быстрее для больших данных;) (однако из-за numpy и т. Д. Не будет столь же эффективным для небольших списков, и это будет намного лучше для Numpy массива, чем списки по очевидным причинам - вы можете даже падение преобразования ...)

x = np.array([-3,2,7,-4,1,-1,1,6,-1,0,-2,1]) 
positive= x>0 
count = np.logical_xor(positive[1:],positive[:-1]).sum() 
count += not(positive[0]) 
print count 

возвращает 8

Xor возвращает истину, если два булевых различны (от + до -) и довольно быстро. Существует одна проблема с кодом: если что-то идет ровно в 0 от положительного знака, оно будет считаться пересечением. Это именно то, что вы задаете в своем вопросе, хотя, поскольку вы представляли 0 как «-».