반복하지만 numpy의 가변 크기 청크
다른 청크를 연결 한 배열이 있습니다.
a = np.array([0, 1, 2, 10, 11, 20, 21, 22, 23])
# > < > < > <
chunks = np.array([3, 2, 4])
repeats = np.array([1, 3, 2])
위의 예에서 새로운 10 년으로 시작하는 각 세그먼트는 반복하고 싶은 별도의 "청크"입니다. 청크 크기와 반복 횟수는 각각에 대해 알려져 있습니다. 나는 다음에 모양 변경 할 수 없습니다 kron
또는 repeat
덩어리가 서로 다른 크기 때문입니다.
내가 원하는 결과는
np.array([0, 1, 2, 10, 11, 10, 11, 10, 11, 20, 21, 22, 23, 20, 21, 22, 23])
# repeats:> 1 < > 3 < > 2 <
이것은 루프에서 수행하기 쉽습니다.
in_offset = np.r_[0, np.cumsum(chunks[:-1])]
out_offset = np.r_[0, np.cumsum(chunks[:-1] * repeats[:-1])]
output = np.zeros((chunks * repeats).sum(), dtype=a.dtype)
for c in range(len(chunks)):
for r in range(repeats[c]):
for i in range(chunks[c]):
output[out_offset[c] + r * chunks[c] + i] = a[in_offset[c] + i]
이것은 다음과 같은 벡터화로 이어집니다.
regions = chunks * repeats
index = np.arange(regions.sum())
segments = np.repeat(chunks, repeats)
resets = np.cumsum(segments[:-1])
offsets = np.zeros_like(index)
offsets[resets] = segments[:-1]
offsets[np.cumsum(regions[:-1])] -= chunks[:-1]
index -= np.cumsum(offsets)
output = a[index]
이 문제를 벡터화하는 더 효율적인 방법이 있습니까? 명확하게하기 위해 코드 검토를 요청하는 것이 아닙니다. 이 함수 호출이 함께 작동하는 방식에 만족합니다. 동일한 결과를 얻기 위해 사용할 수있는 완전히 다른 (보다 효율적인) 함수 호출 조합이 있는지 알고 싶습니다.
이 질문은 영감을받은 내 대답 에 이 질문에 .
답변
이 문제를 해결하는 더 " 수많은 "방법은 다른 대답보다-
np.concatenate(np.repeat(np.split(a, np.cumsum(chunks))[:-1], repeats))
array([ 0, 1, 2, 10, 11, 10, 11, 10, 11, 20, 21, 22, 23, 20, 21, 22, 23])
공지 사항, - 루프에 대한 명시적인.
( np.split
@Divakar가 지적한 암시 적 루프가 있음).
편집 : 벤치 마크 (MacBook pro 13)-
@Mad Physicist가 그의 게시물에서 지적했듯이 Divakar의 솔루션은 더 큰 배열, 청크 및 반복에 대해 더 잘 확장됩니다.

작업을 수행 하는 더 많은 파이썬 방법 (다른 답변보다)은 다음과 같습니다.
result = np.concatenate([ np.tile(tbl, rpt) for tbl, rpt in
zip(np.split(a, np.cumsum(chunks[:-1])), repeats) ])
결과는 다음과 같습니다.
array([ 0, 1, 2, 10, 11, 10, 11, 10, 11, 20, 21, 22, 23, 20, 21, 22, 23])
해당 청크가 범위 배열 인 경우 입력 배열에서 직접 작업 할 수 있으므로 최종 인덱싱 단계를 피할 수 있으므로 개선해야합니다.
# https://stackoverflow.com/a/47126435/ @Divakar
def create_ranges(starts, ends, l):
clens = l.cumsum()
ids = np.ones(clens[-1],dtype=int)
ids[0] = starts[0]
ids[clens[:-1]] = starts[1:] - ends[:-1]+1
out = ids.cumsum()
return out
s = np.r_[0,chunks.cumsum()]
starts = a[np.repeat(s[:-1],repeats)]
l = np.repeat(chunks, repeats)
ends = starts+l
out = create_ranges(starts, ends, l)
정보 제공을 위해 여기에서 작동하는 솔루션을 벤치마킹했습니다. :
def MadPhysicist1(a, chunks, repeats):
in_offset = np.r_[0, np.cumsum(chunks[:-1])]
out_offset = np.r_[0, np.cumsum(chunks[:-1] * repeats[:-1])]
output = np.zeros((chunks * repeats).sum(), dtype=a.dtype)
for c in range(len(chunks)):
for r in range(repeats[c]):
for i in range(chunks[c]):
output[out_offset[c] + r * chunks[c] + i] = a[in_offset[c] + i]
return output
def MadPhysicist2(a, chunks, repeats):
regions = chunks * repeats
index = np.arange(regions.sum())
segments = np.repeat(chunks, repeats)
resets = np.cumsum(segments[:-1])
offsets = np.zeros_like(index)
offsets[resets] = segments[:-1]
offsets[np.cumsum(regions[:-1])] -= chunks[:-1]
index -= np.cumsum(offsets)
output = a[index]
return output
def create_ranges(starts, ends, l):
clens = l.cumsum()
ids = np.ones(clens[-1],dtype=int)
ids[0] = starts[0]
ids[clens[:-1]] = starts[1:] - ends[:-1]+1
out = ids.cumsum()
return out
def Divakar(a, chunks, repeats):
s = np.r_[0, chunks.cumsum()]
starts = a[np.repeat(s[:-1], repeats)]
l = np.repeat(chunks, repeats)
ends = starts+l
return create_ranges(starts, ends, l)
def Valdi_Bo(a, chunks, repeats):
return np.concatenate([np.tile(tbl, rpt) for tbl, rpt in
zip(np.split(a, np.cumsum(chunks[:-1])), repeats)])
def AkshaySehgal(a, chunks, repeats):
return np.concatenate(np.repeat(np.split(a, np.cumsum(chunks))[:-1], repeats))
세 가지 입력 크기에 대한 타이밍을 살펴 보았습니다 : ~ 100, ~ 1000 및 ~ 10k 요소 :
np.random.seed(0xA)
chunksA = np.random.randint(1, 10, size=20) # ~100 elements
repeatsA = np.random.randint(1, 10, size=20)
arrA = np.random.randint(100, size=chunksA.sum())
np.random.seed(0xB)
chunksB = np.random.randint(1, 100, size=20) # ~1000 elements
repeatsB = np.random.randint(1, 10, size=20)
arrB = np.random.randint(100, size=chunksB.sum())
np.random.seed(0xC)
chunksC = np.random.randint(1, 100, size=200) # ~10000 elements
repeatsC = np.random.randint(1, 10, size=200)
arrC = np.random.randint(100, size=chunksC.sum())
다음은 몇 가지 결과입니다.
| | A | B | C |
+---------------+---------+---------+---------+
| MadPhysicist1 | 1.92 ms | 16 ms | 159 ms |
| MadPhysicist2 | 85.5 µs | 153 µs | 744 µs |
| Divakar | 75.9 µs | 95.9 µs | 312 µs |
| Valdi_Bo | 370 µs | 369 µs | 3.4 ms |
| AkshaySehgal | 163 µs | 165 µs | 1.24 ms |