[python] 양수 및 음수 시퀀스의 수와 합산
양수와 음수 시리즈를 계산하고 합산하는 코드를 작성하고 싶습니다.
숫자는 양수 또는 음수 (0이 아님)입니다. 루프
로 코드를 작성했습니다 for
. 창의적인 대안이 있습니까?
데이터
아르 자형
set.seed(100)
x <- round(rnorm(20, sd = 0.02), 3)
파이썬
x = [-0.01, 0.003, -0.002, 0.018, 0.002, 0.006, -0.012, 0.014, -0.017, -0.007,
0.002, 0.002, -0.004, 0.015, 0.002, -0.001, -0.008, 0.01, -0.018, 0.046]
루프
아르 자형
sign_indicator <- ifelse(x > 0, 1,-1)
number_of_sequence <- rep(NA, 20)
n <- 1
for (i in 2:20) {
if (sign_indicator[i] == sign_indicator[i - 1]) {
n <- n + 1
} else{
n <- 1
}
number_of_sequence[i] <- n
}
number_of_sequence[1] <- 1
#############################
summation <- rep(NA, 20)
for (i in 1:20) {
summation[i] <- sum(x[i:(i + 1 - number_of_sequence[i])])
}
파이썬
sign_indicator = [1 if i > 0 else -1 for i in X]
number_of_sequence = [1]
N = 1
for i in range(1, len(sign_indicator)):
if sign_indicator[i] == sign_indicator[i - 1]:
N += 1
else:
N = 1
number_of_sequence.append(N)
#############################
summation = []
for i in range(len(X)):
if number_of_sequence[i] == 1:
summation.append(X[i])
else:
summation.append(sum(X[(i + 1 - number_of_sequence[i]):(i + 1)]))
결과
x n_of_sequence sum
1 -0.010 1 -0.010
2 0.003 1 0.003
3 -0.002 1 -0.002
4 0.018 1 0.018
5 0.002 2 0.020
6 0.006 3 0.026
7 -0.012 1 -0.012
8 0.014 1 0.014
9 -0.017 1 -0.017
10 -0.007 2 -0.024
11 0.002 1 0.002
12 0.002 2 0.004
13 -0.004 1 -0.004
14 0.015 1 0.015
15 0.002 2 0.017
16 -0.001 1 -0.001
17 -0.008 2 -0.009
18 0.010 1 0.010
19 -0.018 1 -0.018
20 0.046 1 0.046
답변
다른 솔루션은 괜찮아 보이지만이 간단한 문제에 대해 복잡한 언어 기능이나 라이브러리 기능을 사용할 필요는 없습니다.
result, prev = [], None
for idx, cur in enumerate(x):
if not prev or (prev > 0) != (cur > 0):
n, summation = 1, cur
else:
n, summation = n + 1, summation + cur
result.append((idx, cur, n, summation))
prev = cur
보시다시피 질문 섹션의 스 니펫에서와 같이 sign_indicator
목록, 두 개의 for 루프 또는 range
기능이 실제로 필요하지 않습니다 .
색인을 1에서 시작하려면 enumerate(x, 1)
대신enumerate(x)
결과를 보려면 다음 코드를 실행할 수 있습니다
for idx, num, length, summation in result:
print(f"{idx: >2d} {num: .3f} {length: >2d} {summation: .3f}")
답변
R에서 data.table
s rleid
를 사용하여 양수와 음수의 일련의 숫자를 가진 그룹을 만든 다음 각 그룹에 일련의 행을 만들고 x
값 의 누적 합계를 수행 할 수 있습니다.
library(data.table)
df <- data.table(x)
df[, c("n_of_sequence", "sum") := list(seq_len(.N), cumsum(x)), by = rleid(sign(x))]
df
# x n_of_sequence sum
# 1: -0.010 1 -0.010
# 2: 0.003 1 0.003
# 3: -0.002 1 -0.002
# 4: 0.018 1 0.018
# 5: 0.002 2 0.020
# 6: 0.006 3 0.026
# 7: -0.012 1 -0.012
# 8: 0.014 1 0.014
# 9: -0.017 1 -0.017
#10: -0.007 2 -0.024
#11: 0.002 1 0.002
#12: 0.002 2 0.004
#13: -0.004 1 -0.004
#14: 0.015 1 0.015
#15: 0.002 2 0.017
#16: -0.001 1 -0.001
#17: -0.008 2 -0.009
#18: 0.010 1 0.010
#19: -0.018 1 -0.018
#20: 0.046 1 0.046
우리가 사용할 수 rleid
있는 dplyr
그룹을 만들뿐만 아니라와 동일한 작업을 수행.
library(dplyr)
df %>%
group_by(gr = data.table::rleid(sign(x))) %>%
mutate(n_of_sequence = row_number(), sum = cumsum(x))
답변
rle
from base
을 사용하여 각 부호의 실행 길이를 계산하고 이와 같은 작업을 수행 할 수 있습니다.
set.seed(0)
z <- round(rnorm(20, sd = 0.02), 3)
run_lengths <- rle(sign(z))$lengths
run_lengths
# [1] 1 1 1 3 1 1 2 2 1 2 2 1 1 1
얻기 위해 n_of_sequence
n_of_sequence <- run_lengths %>% map(seq) %>% unlist
n_of_sequence
# [1] 1 1 1 1 2 3 1 1 1 2 1 2 1 1 2 1 2 1 1 1
마지막으로 시퀀스의 요약을 얻으려면
start <- cumsum(c(1,run_lengths))
start <- start[-length(start)] # start points of each series
map2(start,run_lengths,~cumsum(z[.x:(.x+.y-1)])) %>% unlist()
# [1] -0.010 0.003 -0.002 0.018 0.020 0.026 -0.012 0.014 -0.017 -0.024
# [11] 0.002 0.004 -0.004 0.015 0.017 -0.001 -0.009 0.010 -0.018 0.046
답변
R의 간단한 비 루핑 함수는 다음과 같습니다.
count_and_sum <- function(x)
{
runs <- rle((x > 0) * 1)$lengths
groups <- split(x, rep(1:length(runs), runs))
output <- function(group) data.frame(x = group, n = seq_along(group), sum = cumsum(group))
result <- as.data.frame(do.call(rbind, lapply(groups, output)))
`rownames<-`(result, 1:nrow(result))
}
그래서 당신은 할 수 있습니다 :
set.seed(100)
x <- round(rnorm(20, sd = 0.02), 3)
count_and_sum(x)
#> x n sum
#> 1 -0.010 1 -0.010
#> 2 0.003 1 0.003
#> 3 -0.002 1 -0.002
#> 4 0.018 1 0.018
#> 5 0.002 2 0.020
#> 6 0.006 3 0.026
#> 7 -0.012 1 -0.012
#> 8 0.014 1 0.014
#> 9 -0.017 1 -0.017
#> 10 -0.007 2 -0.024
#> 11 0.002 1 0.002
#> 12 0.002 2 0.004
#> 13 -0.004 1 -0.004
#> 14 0.015 1 0.015
#> 15 0.002 2 0.017
#> 16 -0.001 1 -0.001
#> 17 -0.008 2 -0.009
#> 18 0.010 1 0.010
#> 19 -0.018 1 -0.018
#> 20 0.046 1 0.046
reprex 패키지 (v0.3.0)로 2020-02-16에 작성
답변
간단한 tidyverse
해결책 은 다음과 같습니다 .
library(tidyverse) #or just dplyr and tidyr
set.seed(100)
x <- round(rnorm(20, sd = 0.02), 3)
df <- tibble(x = x) %>%
mutate(seqno = cumsum(c(1, diff(sign(x)) != 0))) %>% #identify sequence ids
group_by(seqno) %>% #group by sequences
mutate(n_of_sequence = row_number(), #count row numbers for each group
sum = cumsum(x)) %>% #cumulative sum for each group
ungroup() %>%
select(-seqno) #remove sequence id
df
# A tibble: 20 x 3
x n_of_sequence sum
<dbl> <int> <dbl>
1 -0.01 1 -0.01
2 0.003 1 0.003
3 -0.002 1 -0.002
4 0.018 1 0.018
5 0.002 2 0.0200
6 0.006 3 0.026
7 -0.012 1 -0.012
8 0.014 1 0.014
9 -0.017 1 -0.017
10 -0.007 2 -0.024
11 0.002 1 0.002
12 0.002 2 0.004
13 -0.004 1 -0.004
14 0.015 1 0.015
15 0.002 2 0.017
16 -0.001 1 -0.001
17 -0.008 2 -0.009
18 0.01 1 0.01
19 -0.018 1 -0.018
20 0.046 1 0.046
답변
파이썬의 경우 누군가 판다 라이브러리를 사용하여 솔루션을 제시합니다. 그 동안 간단한 제안은 다음과 같습니다.
class Combiner:
def __init__(self):
self.index = self.seq_index = self.summation = 0
def combine(self, value):
self.index += 1
if value * self.summation <= 0:
self.seq_index = 1
self.summation = value
else:
self.seq_index += 1
self.summation += value
return self.index, value, self.seq_index, self.summation
c = Combiner()
lst = [c.combine(v) for v in x]
for t in lst:
print(f"{t[0]:3} {t[1]:7.3f} {t[2]:3} {t[3]:7.3f}")
산출:
1 -0.010 1 -0.010
2 0.003 1 0.003
3 -0.002 1 -0.002
4 0.018 1 0.018
5 0.002 2 0.020
6 0.006 3 0.026
7 -0.012 1 -0.012
8 0.014 1 0.014
9 -0.017 1 -0.017
10 -0.007 2 -0.024
11 0.002 1 0.002
12 0.002 2 0.004
13 -0.004 1 -0.004
14 0.015 1 0.015
15 0.002 2 0.017
16 -0.001 1 -0.001
17 -0.008 2 -0.009
18 0.010 1 0.010
19 -0.018 1 -0.018
20 0.046 1 0.046
별도의 목록이 필요하면 할 수 있습니다
idxs, vals, seqs, sums = (list(tpl) for tpl in zip(*lst))
또는 반복자가 괜찮다면 간단히
idxs, vals, seqs, sums = zip(*lst)
( 여기 설명 )
답변
itertools 모듈을 사용하는 Python의 두 가지 다른 게으른 솔루션 .
itertools.groupby 사용 및 누적
from itertools import accumulate, groupby
result = (
item
for _, group in groupby(x, key=lambda n: n < 0)
for item in enumerate(accumulate(group), 1)
)
사용자 정의 누적 함수와 함께 itertools.accumulate 사용
from itertools import accumulate
def sign_count_sum(count_sum, value):
count, prev_sum = count_sum
same_sign = (prev_sum < 0) is (value < 0)
if same_sign:
return count + 1, prev_sum + value
else:
return 1, value
result = accumulate(x, sign_count_sum, initial=(0, 0))
next(result) # needed to skip the initial (0, 0) item
initial
키워드 인수는 파이썬 3.8에서 추가되었다. 이전 버전에서는 사용할 수 있습니다itertools.chain
(0,0) 튜플을 앞에 추가하는 데 .
result = accumulate(chain([(0, 0)], x), sign_count_sum)
예상대로 출력됩니다.
for (i, v), (c, s) in zip(enumerate(x), result):
print(f"{i:3} {v:7.3f} {c:3} {s:7.3f}")
0 -0.010 1 -0.010
1 0.003 1 0.003
2 -0.002 1 -0.002
3 0.018 1 0.018
4 0.002 2 0.020
5 0.006 3 0.026
6 -0.012 1 -0.012
7 0.014 1 0.014
8 -0.017 1 -0.017
9 -0.007 2 -0.024
10 0.002 1 0.002
11 0.002 2 0.004
12 -0.004 1 -0.004
13 0.015 1 0.015
14 0.002 2 0.017
15 -0.001 1 -0.001
16 -0.008 2 -0.009
17 0.010 1 0.010
18 -0.018 1 -0.018
19 0.046 1 0.046