复制matplotlib艺术家 [英] Copy matplotlib artist

查看:153
本文介绍了复制matplotlib艺术家的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用matplotlib创建了一个Line2D对象数组,我想在各种绘图中使用它。但是,由于出现以下情况,无法在多个情节中使用同一位艺术家:

I created an array of Line2D-objects with matplotlib that I'd like to use in various plots. However, using the same artist in multiple plots doesn't work since I get:

RuntimeError: Can not put single artist in more than one figure

我发现,艺术家一旦附加到轴上,就无法附加到另一个上不再。
好​​吧,我的想法是简单地使用copy()复制包含行的数组,但是它不起作用。复制的数组仍然引用相同的对象。我想这是因为您根本无法复制艺术家(?)。

As I found out, the artists once attached to an axis, cannot be attached to another anymore. Well, my idea was to simply copy the array containing the lines with copy() but it won't work. The copied array still refers to the same objects. I suppose, that is because you simply cannot copy artists (?).

有什么方法可以避免重新计算Line2D,而只需计算一次?

Is there any way to avoid the recalculation of the Line2Ds and to only have to calculate them once?

推荐答案

我非常希望在< copy 方法中 matplotlibs 的a href = https://matplotlib.org/3.1.0/api/artist_api.html#artist-class rel = nofollow noreferrer>艺术家类 c $ c>!

I so dearly wish there was a copy method in the Artist Class of matplotlibs!

也许有人想将下面的代码改编为 Artist Class 一种转移方法,用于将一个艺术家对象复制到另一个对象。

Perhaps, someone wants to adapt the code below into the Artist Class in order to have a kind of transfer method to copy one Artist object into another.

使用标准的副本深度复制复制中的对象的code>函数不起作用。

Using the standard copy or deepcopy functions for objects in the package copy does not work.

例如,它没有用脚本:

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

x = np.linspace(0, 2*np.pi, 100)

fig  = plt.figure()
ax1 = plt.subplot(211)
ax2 = plt.subplot(212)

# create a Line2D object
line1, = ax1.plot(x, np.sin(x))

# copy the Line2D object 
line2 = copy.deepcopy(line1) #ERROR!



解决方案:



因此,唯一的我发现要复制 Artist 对象的方法是,创建所需类型的空对象,然后通过循环将所有必要的属性从原始对象转移到新创建的对象中

Solutions:

So, the only way I found to copy an Artist object, is to create an empty object of the desired type, and then transfer via loop all necessary attributes from the original object into the new created one.

我定义的此函数是将属性值从一个对象传输到另一个对象的工具:

The tool to transfer attributes values from one object to the other is this function I defined:

# -- Function to attributes copy
#It copies the attributes given in attr_list (a sequence of the attributes names as
#  strings) from the object 'obj1' into the object 'obj2'
#It should work for any objects as long as the attributes are accessible by
# 'get_attribute' and 'set_attribute' methods.

def copy_attributes(obj2, obj1, attr_list):
        for i_attribute  in attr_list:
            getattr(obj2, 'set_' + i_attribute)( getattr(obj1, 'get_' + i_attribute)() )

在此函数中,最重要的参数是 attr_list 。这是我们要从 obj1 复制到 obj2 的属性名称的列表,例如对象 Artist.Line2D ,它可能是 attr_list =('xdata','ydata','动画','抗锯齿','颜色' ,'dash_capstyle','dash_joinstyle','drawstyle')

In this function the most important parameter is attr_list. This is a list of the names of the attributes that we want to copy from obj1 into obj2, for example, for an object Artist.Line2D, it could be attr_list = ('xdata', 'ydata', 'animated', 'antialiased', 'color', 'dash_capstyle', 'dash_joinstyle', 'drawstyle')

与任何 Artist 生成具有要传输的正确属性的属性名称列表。

As any Artist object has different attributes, the key in this transfer process is to generate the list of attributes names with the right attributes to be transferred.

生成属性名称列表的方法有两种:

There are two ways to generate the list of the attributes names:


  • 第一个选项:我们指定要选择的属性。也就是说,我们硬代码包含要传输的所有属性的列表。它比第二种选择更为艰巨。我们必须为每种类型的对象完全指定属性:这些通常是很长的列表。仅当我们只处理一种类型的 Artist 对象时,才建议这样做。

  • First option: we specify the attributes to be selected. That is, we hard code a list with all attributes we want to transfer. It is more arduous than the second option. We have to fully specify the attributes for every type of object: these are usually long lists. It is only recommendable, when we deal with only one type of Artist object.

第二个选项:我们指定未未选择的属性。也就是说,我们编写了一个例外列表,其中包含我们不想转移的属性,我们会自动选择对象的所有可转移属性,但会选择例外列表中的那些属性。这是最快的选项,我们可以同时将其用于不同类型的 Artist 对象。

Second option: we specify the attributes that are not selected. That is, we write an "exceptions list" with the attributes that we do not want to transfer, we automatically choose all transferable attributes of the object, but those in our "exception list". This is the quickest option, and we can use it with different types of Artist objects simultaneously.

我们只需编写一个赋值即可定义要传输的属性列表,如下所示:

We just write an assignment to define the list of attributes we want to transfer, as we have shown above.

此选项的缺点是无法立即扩展到不同的 Artist 对象,例如Line2D和Circle。因为我们必须对不同的属性名称列表进行硬编码,所以每种类型的 Artist 对象都需要一个。

The drawback of this option, is that it cannot be immediately extended to different Artist objects, for instance, Line2D and Circle. Because we have to hard code different lists of attributes names, one for each type of Artist object.

我将显示Line2D Artist类的示例,如所指定的问题。

I show an example for Line2D Artist class, as in the question specified.

import matplotlib.pyplot as plt
import numpy as np

# -- Function to attributes copy
#It copies the attributes given in attr_list (a sequence of the attributes names as
#  strings) from the object 'obj1' into the object 'obj2'
#It should work for any objects as long as the attributes are accessible by
# 'get_attribute' and 'set_attribute' methods.
def copy_attributes(obj2, obj1, attr_list):
    for i_attribute  in attr_list:
        getattr(obj2, 'set_' + i_attribute)( getattr(obj1, 'get_' + i_attribute)() )

#Creating a figure with to axes
fig  = plt.figure()
ax1 = plt.subplot(211)
ax2 = plt.subplot(212)
plt.tight_layout() #Tweak to improve subplot layout

#create a Line2D object 'line1' via plot
x = np.linspace(0, 2*np.pi, 100)
line1, = ax1.plot(x, np.sin(x))

ax1.set_xlabel('line1 in ax1') #Labelling axis

#Attributes of the old Line2D object that must be copied to the new object
#It's just a strings list, you can add or take away attributes to your wishes
copied_line_attributes = ('xdata', 'ydata', 'animated', 'antialiased', 'color',  
                    'dash_capstyle', 'dash_joinstyle', 
                    'drawstyle', 'fillstyle', 'linestyle', 'linewidth',
                    'marker', 'markeredgecolor', 'markeredgewidth', 'markerfacecolor',
                    'markerfacecoloralt', 'markersize', 'markevery', 'pickradius',
                    'solid_capstyle', 'solid_joinstyle', 'visible', 'zorder')

#Creating an empty Line2D object
line2 = plt.Line2D([],[])

#Copying the list of attributes 'copied_line_attributes' of line1 into line2
copy_attributes(line2, line1, copied_line_attributes)

#Setting the new axes ax2 with the same limits as ax1
ax2.set_xlim(ax1.get_xlim())
ax2.set_ylim(ax1.get_ylim())

#Adding the copied object line2 to the new axes
ax2.add_artist(line2)

ax2.set_xlabel('line2 in ax2') #Labelling axis

plt.show()



输出



Output

在这种情况下,我们指定th的名称这些我们不想转移的属性:我们制作了一个例外列表。我们自动收集 Artist 对象的所有 transferable 属性,并排除例外列表的名称

In this case, we specify the names of the attributes, that we do not want to transfer: we make a exception list. We gather automatically all transferable attributes of the Artist object and exclude the names of our exception list.

优点是通常对于不同的 Artist 对象,排除的属性是相同的短列表,因此,此选项可以更快速地编写脚本。在下面的示例中,列表简短至 except_attributes =('transform','figure')

The advantage is that usually for different Artist objects the excluded attributes are the same short list and, consequently, this option can be more quickly scripted. In the example below, the list is as short as except_attributes = ('transform', 'figure')

在这种情况下,关键功能是 list_transferable_attributes ,如下所示:

The key function in this case is list_transferable_attributes as shown below:

#Returns a list of the transferable attributes, that is the attributes having
# both a 'get' and 'set' method. But the methods in 'except_attributes' are not
# included
def list_transferable_attributes(obj, except_attributes):
    obj_methods_list = dir(obj)

    obj_get_attr = []
    obj_set_attr = []
    obj_transf_attr =[]

    for name in obj_methods_list:
        if len(name) > 4:
            prefix = name[0:4]
            if prefix == 'get_':
                obj_get_attr.append(name[4:])
            elif prefix == 'set_':
                obj_set_attr.append(name[4:])

    for attribute in obj_set_attr:
        if attribute in obj_get_attr and attribute not in except_attributes:
            obj_transf_attr.append(attribute)

    return obj_transf_attr



完整示例



Full Example

import matplotlib.pyplot as plt
import numpy as np

# -- Function to copy, or rather, transfer, attributes
#It copies the attributes given in attr_list (a sequence of the attributes names as
#  strings) from the object 'obj1' into the object 'obj2'
#It should work for any objects as long as the attributes are accessible by
# 'get_attribute' and 'set_attribute' methods.
def copy_attributes(obj2, obj1, attr_list):
    for i_attribute  in attr_list:
        getattr(obj2, 
                'set_' + i_attribute)( getattr(obj1, 'get_' + i_attribute)() )

# #Returns a list of pairs (attribute string, attribute value) of the given 
# # attributes list 'attr_list' of the given object 'obj'                
# def get_attributes(obj, attr_list):
#     attr_val_list = []
#     for i_attribute  in attr_list:
#         i_val = getattr(obj, 'get_' + i_attribute)()
#         attr_val_list.append((i_attribute, i_val))
#     
#     return attr_val_list

#Returns a list of the transferable attributes, that is the attributes having
# both a 'get' and 'set' method. But the methods in 'except_attributes' are not
# included
def list_transferable_attributes(obj, except_attributes):
    obj_methods_list = dir(obj)

    obj_get_attr = []
    obj_set_attr = []
    obj_transf_attr =[]

    for name in obj_methods_list:
        if len(name) > 4:
            prefix = name[0:4]
            if prefix == 'get_':
                obj_get_attr.append(name[4:])
            elif prefix == 'set_':
                obj_set_attr.append(name[4:])

    for attribute in obj_set_attr:
        if attribute in obj_get_attr and attribute not in except_attributes:
            obj_transf_attr.append(attribute)

    return obj_transf_attr


#Creating a figure with to axes
fig  = plt.figure()
ax1 = plt.subplot(211) #First axes
ax2 = plt.subplot(212) #Second axes
plt.tight_layout() #Optional: Tweak to improve subplot layout

#create an artist Line2D object 'line1' via plot
x = np.linspace(0, 2*np.pi, 100)
line1, = ax1.plot(x, np.sin(x))

#create an artist Circle object
circle1 = plt.Circle([1,0], 0.5, facecolor='yellow', edgecolor='k')
#Adding the object to the first axes
ax1.add_patch(circle1)

#Labelling first axis
ax1.set_xlabel('line1 and circle1 in ax1') 

#Methods that we should not copy from artist to artist
except_attributes = ('transform', 'figure')

#Obtaining the names of line2D attributes that can be transfered 
transferred_line_attributes = list_transferable_attributes(line1, except_attributes)

#Obtaining the names of Circle attributes that can be transfered
transferred_circle_attributes = list_transferable_attributes(circle1, except_attributes)

#Creating an empty Line2D object
line2 = plt.Line2D([],[])
circle2 = plt.Circle([],[])

#Copying the list of attributes 'transferred_line_attributes' of line1 into line2
copy_attributes(line2, line1, transferred_line_attributes)
copy_attributes(circle2, circle1, transferred_circle_attributes)

#attr_val_list_line2 = get_attributes(line2, line1_attr_list)

#Setting the new axes ax2 with the same limits as ax1
ax2.set_xlim(ax1.get_xlim())
ax2.set_ylim(ax1.get_ylim())

#Adding the copied object line2 to the new axes
ax2.add_line(line2) #.add_artist(line2) also possible
ax2.add_patch(circle2) #.add_artist(circle2) also possible

ax2.set_xlabel('line2 and circle2 in ax2') #Labelling axis

plt.show()



输出



Output

这篇关于复制matplotlib艺术家的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆