2012-11-29 2 views
5

Python. matplotlib : 많은 수의 선분을 독립적 인 그라데이션으로 효율적으로 채색하는 방법은 무엇입니까?
이미 thisthis 및 기타 내용을 읽으십시오. 그들 중 누구도 우리의 대답이 아닙니다!Matplotlib : 많은 수의 선분을 독립적 인 그라데이션으로 효율적으로 채색하는 방법

그라디언트 색상으로 각각 플롯하기를 원하는 여러 줄이 있습니다.

위의 첫 번째 링크에서 언급 한 해결책은 둘 이상의 줄이있는 경우 작동하지 않습니다. 즉, 색상주기를 변경하면 플롯의 모든 것이 관심의 대상이되지 않습니다. 이것은 우리의 관심이 전혀 아닙니다.

matplotlib 사이트의 두 번째 링크는 각 줄을 여러 개로 분할하여 사용합니다. 이것은 좋은 접근법이 아닙니다. 왜냐하면 엄청난 수의 라인, 말하자면, 10000 또는 그 이상의 것이기 때문입니다; 한 줄에 10 개의 세그먼트 만 선택하더라도 결과는 너무 큽니다! 그럼에도 불구하고 그 결과로 생긴 선들은 매끄럽게 착색되지 않습니다! 더 나은 그래디언트를 위해 세그먼트 수를 선분의 함수로 만들면 그 결과는 매우 커집니다! 표시하기 어렵고 파일로 저장하기가 어렵습니다.

+2

을 당신이 matplotlib''의 제한을 공격 한 것으로 생각하지만, 생각하지 않는다 그것이 당신의 주된 문제입니다. 만약 당신이 10k 라인을 가지고 있다면, 비록 1px 너비로 그 라인을 꾸몄다 할지라도, 그것들을 일렬로 세우는 것은 실제로 그것들을 모두 독립적으로 볼 수있는 거대한 디스플레이/프린트를 필요로 할 것입니다. 이 음모를 꾸미는 방법을 찾을 수 있다고해도 합리적인 방법으로는 볼 수 없습니다. 자신의 데이터를 거칠게 그릴 수있는 방법이 있습니까? – tacaswell

+0

10000 개의 별도 라인 콜렉션 대신 단일 라인 콜렉션을 추가해 보셨습니까? 여전히 느리지 만 빠릅니다. 패닝 (panning) 및 확대/축소 (zooming)시에도 반응이 좋습니다. –

+0

@JoeKington : 멋지다. – Developer

답변

6

하나의 (사소한) 속도 향상은 별도의 10000 회선 모음 대신 단일 회선 모음을 추가하는 것입니다.

모든 라인이 동일한 색상 맵을 공유하는 한, 이들을 하나의 라인 컬렉션으로 그룹화 할 수 있으며 각 라인은 여전히 ​​독립적 인 그래디언트를 가질 수 있습니다.

Matplotlib은 이런 종류의 작업에 여전히 느립니다. 빠른 드로잉 시간보다는 품질 출력에 최적화되어 있습니다. 그러나 속도를 조금 높일 수 있습니다 (~ 3 배). (?)

그래서, 당신은 아마하다고 생각하는 방법의 예로서 지금 그 일을 대신

import numpy as np 
import matplotlib.pyplot as plt 
from matplotlib.collections import LineCollection 
# Make random number generation consistent between runs 
np.random.seed(5) 

def main(): 
    numlines, numpoints = 2, 3 
    lines = np.random.random((numlines, numpoints, 2)) 

    fig, ax = plt.subplots() 
    for line in lines: 
     # Add "num" additional segments to the line 
     segments, color_scalar = interp(line, num=20) 
     coll = LineCollection(segments) 
     coll.set_array(color_scalar) 
     ax.add_collection(coll) 
    plt.show() 

def interp(data, num=20): 
    """Add "num" additional points to "data" at evenly spaced intervals and 
    separate into individual segments.""" 
    x, y = data.T 
    dist = np.hypot(np.diff(x - x.min()), np.diff(y - y.min())).cumsum() 
    t = np.r_[0, dist]/dist.max() 

    ti = np.linspace(0, 1, num, endpoint=True) 
    xi = np.interp(ti, t, x) 
    yi = np.interp(ti, t, y) 

    # Insert the original vertices 
    indices = np.searchsorted(ti, t) 
    xi = np.insert(xi, indices, x) 
    yi = np.insert(yi, indices, y) 

    return reshuffle(xi, yi), ti 

def reshuffle(x, y): 
    """Reshape the line represented by "x" and "y" into an array of individual 
    segments.""" 
    points = np.vstack([x, y]).T.reshape(-1,1,2) 
    points = np.concatenate([points[:-1], points[1:]], axis=1) 
    return points 

if __name__ == '__main__': 
    main() 

, 나는이 라인을 따라 뭔가를하고 두시길 것 (유일한 차이는 main 기능에)

enter image description here

,536 :
import numpy as np 
import matplotlib.pyplot as plt 
from matplotlib.collections import LineCollection 
# Make random number generation consistent between runs 
np.random.seed(5) 

def main(): 
    numlines, numpoints = 2, 3 
    points = np.random.random((numlines, numpoints, 2)) 

    # Add "num" additional segments to each line 
    segments, color_scalar = zip(*[interp(item, num=20) for item in points]) 

    segments = np.vstack(segments) 
    color_scalar = np.hstack(color_scalar) 

    fig, ax = plt.subplots() 
    coll = LineCollection(segments) 
    coll.set_array(color_scalar) 
    ax.add_collection(coll) 

    plt.show() 

def interp(data, num=20): 
    """Add "num" additional points to "data" at evenly spaced intervals and 
    separate into individual segments.""" 
    x, y = data.T 
    dist = np.hypot(np.diff(x - x.min()), np.diff(y - y.min())).cumsum() 
    t = np.r_[0, dist]/dist.max() 

    ti = np.linspace(0, 1, num, endpoint=True) 
    xi = np.interp(ti, t, x) 
    yi = np.interp(ti, t, y) 

    # Insert the original vertices 
    indices = np.searchsorted(ti, t) 
    xi = np.insert(xi, indices, x) 
    yi = np.insert(yi, indices, y) 

    return reshuffle(xi, yi), ti 

def reshuffle(x, y): 
    """Reshape the line represented by "x" and "y" into an array of individual 
    segments.""" 
    points = np.vstack([x, y]).T.reshape(-1,1,2) 
    points = np.concatenate([points[:-1], points[1:]], axis=1) 
    return points 

if __name__ == '__main__': 
    main() 

두 버전은 동일한 그래프를 생성

줄 수를 10000 개로 늘리면 성능이 크게 차이가납니다.

Took 10.866694212 sec with a single collection 
Took 28.594727993 sec with multiple collections 
3 점씩 10000 개 라인, 및 색상 그라데이션 걸쳐 보간 추가로 20 점 (각 라인에 23 세그먼트)를 이용하고는 PNG로도 저장하는 데 걸리는 시간을 찾고

그래서 한 줄 모음을 사용하면이 특별한 경우에 3 배 빠른 속도를 낼 수 있습니다. 별은 아니지만 아무것도없는 것보다 낫습니다.

다음은 타이밍 코드와 출력 수치입니다 (출력 수치는 그림의 순서가 다르기 때문에 동일하지 않습니다.당신은 Z-수준을 제어해야 할 경우) 별도의 행 컬렉션에 충실해야합니다 :

enter image description here

import numpy as np 
import matplotlib.pyplot as plt 
from matplotlib.collections import LineCollection 
import time 
# Make random number generation consistent between runs 
np.random.seed(5) 

def main(): 
    numlines, numpoints = 10000, 3 
    lines = np.random.random((numlines, numpoints, 2)) 

    # Overly simplistic timing, but timeit is overkill for this exmaple 
    tic = time.time() 
    single_collection(lines).savefig('/tmp/test_single.png') 
    toc = time.time() 
    print 'Took {} sec with a single collection'.format(toc-tic) 

    tic = time.time() 
    multiple_collections(lines).savefig('/tmp/test_multiple.png') 
    toc = time.time() 
    print 'Took {} sec with multiple collections'.format(toc-tic) 

def single_collection(lines): 
    # Add "num" additional segments to each line 
    segments, color_scalar = zip(*[interp(item, num=20) for item in lines]) 
    segments = np.vstack(segments) 
    color_scalar = np.hstack(color_scalar) 

    fig, ax = plt.subplots() 
    coll = LineCollection(segments) 
    coll.set_array(color_scalar) 
    ax.add_collection(coll) 
    return fig 

def multiple_collections(lines): 
    fig, ax = plt.subplots() 
    for line in lines: 
     # Add "num" additional segments to the line 
     segments, color_scalar = interp(line, num=20) 
     coll = LineCollection(segments) 
     coll.set_array(color_scalar) 
     ax.add_collection(coll) 
    return fig 

def interp(data, num=20): 
    """Add "num" additional points to "data" at evenly spaced intervals and 
    separate into individual segments.""" 
    x, y = data.T 
    dist = np.hypot(np.diff(x - x.min()), np.diff(y - y.min())).cumsum() 
    t = np.r_[0, dist]/dist.max() 

    ti = np.linspace(0, 1, num, endpoint=True) 
    xi = np.interp(ti, t, x) 
    yi = np.interp(ti, t, y) 

    # Insert the original vertices 
    indices = np.searchsorted(ti, t) 
    xi = np.insert(xi, indices, x) 
    yi = np.insert(yi, indices, y) 

    return reshuffle(xi, yi), ti 

def reshuffle(x, y): 
    """Reshape the line represented by "x" and "y" into an array of individual 
    segments.""" 
    points = np.vstack([x, y]).T.reshape(-1,1,2) 
    points = np.concatenate([points[:-1], points[1:]], axis=1) 
    return points 

if __name__ == '__main__': 
    main() 
+0

고마워. 정말로 멋진 트릭입니다. 우리는 귀하의 완전한 답변을 수락합니다. 우리는 이것이 세분화 개념을 기반으로 할 수있는 가장 많은 것이 될 것이라고 확신합니다. 우리는 우리의 어플리케이션에서 연결된 삼각형 메쉬가 연결된 꼭지점의 값에 따라 색을 칠하는 것이 중요하다는 것을 여기서 언급하고자한다. 우리는 당신의 노력에 감사드립니다. – Developer

+0

http://stackoverflow.com/a/10253183/2851664이 솔루션은 보간이 필요하지 않으며 훨씬 빠르며 더 멋진 플롯을 생성합니다. – sebix

+0

@sebix -이 솔루션은이 솔루션과 동일합니다. 보간은 선택 사항입니다. 원래 정점 사이에서 색상을 부드럽게 전환하려면이 작업이 필요합니다. –

관련 문제