2015-09-07 22 views
7

Tôi đang cố gắng vẽ một vài (nhiều nghìn) đối tượng hình tròn - tôi không có nhiều kinh nghiệm làm việc với python. Tôi quan tâm đến việc xác định vị trí, bán kính và màu sắc. Có cách nào hiệu quả hơn để đạt được kết quả tương tự ?:Làm thế nào tôi có thể vẽ lô hàng ngàn vòng tròn một cách nhanh chóng?

import matplotlib.pyplot as plt 

xvals = [0,.1,.2,.3] 
yvals = [0,.1,.2,.3] 
rvals = [0,.1,.1,.1] 

c1vals = [0,.1,0..1] 
c2vals = [.1,0,.1,0] 
c3vals = [.1,.1,.1,.1] 

for q in range(0,4): 
    circle1=plt.Circle((xvals[q], yvals[q]), rvals[q], color=[0,0,0]) 
    plt.gcf().gca().add_artist(circle1) 
+2

Bạn muốn sử dụng một 'EllipseCollection' có thể làm những gì bạn muốn http://matplotlib.org/examples/pylab_examples/ellipse_collection.html – tacaswell

Trả lời

8

Mấu chốt ở đây là sử dụng một Collection. Trong trường hợp của bạn, bạn muốn tạo một PatchCollection.

Matplotlib tối ưu hóa việc vẽ nhiều nghệ sĩ tương tự thông qua việc sử dụng bộ sưu tập. Nó nhanh hơn nhiều so với việc vẽ từng cái một. Hơn nữa, cốt truyện sẽ không chứa hàng ngàn nghệ sĩ riêng lẻ, chỉ có một bộ sưu tập. Điều này tăng tốc độ nhiều hoạt động linh tinh khác cần phải hoạt động trên mỗi nghệ sĩ mỗi lần vẽ.

scatter thực sự là nhiều hơn nhanh hơn phương pháp hiện tại của bạn vì sẽ thêm bộ sưu tập thay vì các nghệ sĩ riêng biệt. Tuy nhiên, nó cũng vẽ các điểm đánh dấu với kích thước không nằm trong tọa độ dữ liệu.

Để khắc phục điều đó, bạn có thể sử dụng cùng cách tiếp cận scatter, nhưng tạo bộ sưu tập theo cách thủ công.

Như một ví dụ:

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.collections 

num = 5000 
sizes = 0.2 * np.random.random(num) 
xy = 50 * np.random.random((num, 2)) 

# Note that the patches won't be added to the axes, instead a collection will 
patches = [plt.Circle(center, size) for center, size in zip(xy, sizes)] 

fig, ax = plt.subplots() 

coll = matplotlib.collections.PatchCollection(patches, facecolors='black') 
ax.add_collection(coll) 

ax.margins(0.01) 
plt.show() 

enter image description here

này làm khá suôn sẻ đối với tôi.Chỉ để chứng minh rằng các vòng tròn là ở tọa độ dữ liệu, lưu ý những gì sẽ xảy ra nếu chúng ta phóng to trên một hình chữ nhật hẹp (lưu ý: điều này giả định rằng các khía cạnh của cốt truyện được thiết lập để auto):

enter image description here


Nếu bạn thực sự tập trung vào tốc độ, bạn có thể sử dụng EllipseCollection như @tcaswell đề xuất.

An EllipseCollection sẽ chỉ thực hiện một đường dẫn, nhưng sẽ mở rộng và dịch tại thời điểm vẽ ở vị trí/kích thước bạn chỉ định.

Nhược điểm là mặc dù kích thước có thể ở tọa độ dữ liệu, vòng tròn sẽ luôn là một vòng tròn, ngay cả khi tỷ lệ co của ô không phải là 1. (nghĩa là vòng tròn sẽ không căng ra hình trên).

Lợi thế là nó nhanh.

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.collections 

num = 5000 
sizes = 0.4 * np.random.random(num) 
xy = 50 * np.random.random((num, 2)) 

fig, ax = plt.subplots() 

coll = matplotlib.collections.EllipseCollection(sizes, sizes, 
               np.zeros_like(sizes), 
               offsets=xy, units='x', 
               transOffset=ax.transData, 
               **kwargs) 
ax.add_collection(coll) 
ax.margins(0.01) 
plt.show() 

enter image description here

Chú ý sự khác biệt khi chúng ta phóng to trên một khu vực tương tự với con số thứ hai. Các vòng tròn trở nên lớn hơn (kích thước nằm trong tọa độ dữ liệu), nhưng vẫn là các vòng tròn thay vì kéo dài. Chúng không phải là biểu diễn chính xác của một vòng tròn trong không gian "dữ liệu".

enter image description here

Để đưa ra một số ý tưởng về sự khác biệt thời gian, đây là thời gian để tạo ra và vẽ một con số với cùng 5000 vòng tròn với nhau trong ba phương pháp:

In [5]: %timeit time_plotting(circles) 
1 loops, best of 3: 3.84 s per loop 

In [6]: %timeit time_plotting(patch_collection) 
1 loops, best of 3: 1.37 s per loop 

In [7]: %timeit time_plotting(ellipse_collection) 
1 loops, best of 3: 228 ms per loop 
+2

Câu hỏi này cũng nhắc https://github.com/matplotlib/matplotlib/pull/5035 vì nó _seems_ giống như 'CircleCollection' nên làm những gì bạn muốn, nhưng nó được mã hóa cứng là khu vực ở điểm^2. – tacaswell

+0

Đây là một câu trả lời tuyệt vời. Tôi sẽ chỉnh sửa với các bản dùng thử thời gian, nhưng bạn đã đánh tôi với nó :). FWIW, tôi đã so sánh các vòng tròn so với patch_collection và có tỷ lệ số tương tự. Cảm ơn! – ConfusinglyCuriousTheThird

+0

@tcaswell - Rất tuyệt! Cảm ơn vì điều đó! –

3

scatter có lẽ tốt hơn cho bạn hơn plt.Circle mặc dù nó sẽ không làm bất cứ điều gì chạy nhanh hơn.

for i in range(4): 
    mp.scatter(xvals[i], yvals[i], s=rvals[i]) 

Nếu bạn có thể xử lý các vòng tròn có cùng kích thước thì mp.plot(xvals[i], yvals[i], marker='o') sẽ có hiệu suất cao hơn.

Nhưng điều này có thể là giới hạn matplotlib, thay vì giới hạn ngôn ngữ. Có các thư viện JavaScript tuyệt vời để vẽ hàng ngàn điểm dữ liệu một cách hiệu quả (d3.js). Có thể ai đó ở đây sẽ biết một người mà bạn có thể gọi từ Python.

+0

Thật không may, phân tán không cho phép bạn chỉ định bán kính trong các đơn vị" dữ liệu ", mà thay vào đó lấy đầu vào theo pixel. – ConfusinglyCuriousTheThird

1

Bạn chắc chắn muốn di chuyển ...gca() bên ngoài vòng lặp của mình. Bạn cũng có thể sử dụng tính năng đọc danh sách.

fig = plt.figure() 
ax = plt.gcf().gca() 

[ax.add_artist(plt.Circle((xvals[q],yvals[q]),rvals[q],color=[0,0,0])) 
for q in xrange(4)] # range(4) for Python3 

Dưới đây là một số xét nghiệm để tạo ra 4.000 vòng tròn bằng cách sử dụng phương pháp khác nhau:

xvals = [0,.1,.2,.3] * 1000 
yvals = [0,.1,.2,.3] * 1000 
rvals = [0,.1,.1,.1] * 1000 

%%timeit -n5 fig = plt.figure(); ax = plt.gcf().gca() 
for q in range(4000): 
    circle1=plt.Circle((xvals[q], yvals[q]), rvals[q], color=[0,0,0]) 
    plt.gcf().gca().add_artist(circle1) 
5 loops, best of 3: 792 ms per loop 

%%timeit -n5 fig = plt.figure(); ax = plt.gcf().gca() 
for q in xrange(4000): 
    ax.add_artist(plt.Circle((xvals[q],yvals[q]),rvals[q],color=[0,0,0])) 
5 loops, best of 3: 779 ms per loop 

%%timeit -n5 fig = plt.figure(); ax = plt.gcf().gca() 
[ax.add_artist(plt.Circle((xvals[q],yvals[q]),rvals[q],color=[0,0,0])) for q in xrange(4000)] 
5 loops, best of 3: 730 ms per loop 
+0

điều này có vẻ đúng hướng cho những gì tôi muốn ... những gì chính xác là danh sách hiểu? – ConfusinglyCuriousTheThird

+0

Việc hiểu danh sách thường hiệu quả hơn các vòng lặp 'for'. https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions – Alexander

1

Không chắc những gì bạn đang thực sự cố gắng để làm, hoặc những vấn đề hoặc mối quan tâm của bạn, nhưng đây là một hoàn toàn phương pháp khác nhau của âm mưu vòng tròn ... tạo một file SVG như thế này và gọi nó là circles.svg

<?xml version="1.0" standalone="no"?> 
<svg width="500" height="300" version="1.1" xmlns="http://www.w3.org/2000/svg"> 
    <circle cx="100" cy="175" r="200" stroke="lime" fill="coral" stroke-width="28"/> 
    <circle cx="25" cy="75" r="80" stroke="red" fill="yellow" stroke-width="5"/> 
    <circle cx="400" cy="280" r="20" stroke="black" fill="blue" stroke-width="10"/> 
</svg> 

và vượt qua nó để ImageMagick để làm thành một PNG tập tin như thế này:

convert circles.svg result.png 

enter image description here

+0

cảm ơn đề xuất. Đây là một phần của một dự án lớn hơn, nơi tôi muốn thao tác dữ liệu, vì vậy tôi muốn giữ nó trong Python. – ConfusinglyCuriousTheThird

+1

Để downvoter ... nếu bạn đang đi để downvote, ít nhất bạn có thể có lịch sự để giải thích lý do tại sao để tất cả chúng ta có thể tìm hiểu một cái gì đó. Trong các thông số của câu hỏi của OP, đây là một câu trả lời hoàn toàn hợp lý. –

Các vấn đề liên quan