質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.33%

RuntimeError: Can not put single artist in more than one figureの解決の仕方がわからないです.

受付中

回答 0

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 363

RyosukeSHIBATA

score 11

概要

pythonを使ってグラフをいっぱい書くプログラムを試しているのですが.
RuntimeError: Can not put single artist in more than one figure
というエラーが起きて困っています.

問題箇所

https://stackoverrun.com/ja/q/12300089
こちらのリンクでplt.figureを複数設定して解決されている話はあるのですが,
今のプログラムでうまくplt.figureを複数生成する方法が思いつきません.

def evalOneMax(ind):
    """Objective function."""
    evaluation = 0
    for dish in ind:
        evaluation += dish.calorie
    # TODO:グラフ領域を設定

    #TODO:いい感じにトレーのサイズを外から引数として撮ってくるやり方がわからない直接書いた

    tray_size = np.array([370, 260])
    fig = plt.figure()
    axis = fig.add_subplot(1, 1, 1, xlim=tray_size[0], ylim=tray_size[1], aspect='equal')
    for dish in ind:
        axis.add_patch(dish.fig)#<-----ここでエラーが発生
    axis.axis("off")
    plt.savefig('tmp.png')
    fig.delaxes(axis)
    plt.clf()
    plt.cla()
    plt.close()
    del axis
    gray_img = cv2.imread('tmp.png')
    ind.img = gray_img
    ret, dishes_area_image = cv2.threshold(gray_img, 200, 255, cv2.THRESH_BINARY)#図形がある
    dishes_area = cv2.countNonZero(cv2.bitwise_not(cv2.cvtColor(dishes_area_image, cv2.COLOR_RGB2GRAY)))
    cv2.imwrite('dishes_area.png',dishes_area_image)
    ret, dishes_overlap_area_image = cv2.threshold(gray_img, 100, 255, cv2.THRESH_BINARY)#重なってて黒い
    cv2.imwrite('dishes_overlap_area.png',dishes_overlap_area_image)
    dishes_overlap_area = cv2.countNonZero(cv2.bitwise_not(cv2.cvtColor(dishes_overlap_area_image, cv2.COLOR_RGB2GRAY)))
    overlap_per = 1 - dishes_overlap_area/dishes_area

    return evaluation * overlap_per

プログラム全体

import random
import numpy as np
from operator import attrgetter
import csv
import matplotlib
import matplotlib.pyplot as plt
import cv2
#


def main():
    n_gene = 4  # The number of genes.
    n_ind = 300  # The number of individuals in a population.
    CXPB = 0.5  # The probability of crossover.
    MUTPB = 0.2  # The probability of individdual mutation.
    MUTINDPB = 0.05  # The probability of gene mutation.
    NGEN = 40  # The number of generation loop.
    dish_types= DishTypes()
    dish_types.load_file()
    random.seed(64)
    # --- Step1 : Create initial generation.
    pop = create_pop(n_ind, n_gene, dish_types)
    set_fitness(evalOneMax, pop)
    best_ind = max(pop, key=attrgetter("fitness"))

    # --- Generation loop.
    print("Generation loop start.")
    print("Generation: 0. Best fitness: " + str(best_ind.fitness))
    fig_save(best_ind, '{:0=5}'.format(0))
    for g in range(NGEN):

        # --- Step2 : Selection.
        offspring = selTournament(pop, n_ind, tournsize=3)

        # --- Step3 : Crossover.
        crossover = []
        for child1, child2 in zip(offspring[::2], offspring[1::2]):# (偶数個目,奇数個目)で子孫を作成
            if random.random() < CXPB:
                child1, child2 = cxTwoPointCopy(child1, child2)
                # child1.fitness = None
                # child2.fitness = None
            crossover.append(child1)
            crossover.append(child2)

        offspring = crossover[:]

        # --- Step4 : Mutation.
        mutant = []
        for mut in offspring:
            if random.random() < MUTPB:
                mut = mutFlipBit(dish_types, mut, indpb=MUTINDPB)
                # mut.fitness = None
            mutant.append(mut)

        offspring = mutant[:]

        # --- Update next population.
        pop = offspring[:]
        set_fitness(evalOneMax, pop)

        # --- Print best fitness in the population.
        best_ind = max(pop, key=attrgetter("fitness"))
        print("Generation: " + str(g + 1) + ". Best fitness: " + str(best_ind.fitness))
        fig_save(best_ind, dish_types, '{:0=5}'.format(g + 1))
    print("Generation loop ended. The best individual: ")
    print(best_ind)


class DishTypes:
    def __init__(self):
        self.num=0
        self.type=[]
        self.size=[]
        self.name=[]
        self.calorie=[]
        self.protein=[]
        self.lipid=[]
        self.carbohydrate=[]
        self.calcium=[]
        self.price=[]
        self.tray_size=np.array([370, 260])
    def load_file(self):
        with open('dish.csv') as f:
            reader = csv.reader(f)
            for row in reader:
                self.name.append(row[0])
                self.type.append(int(row[1]))
                self.size.append(int(row[2]))
                self.calorie.append(float(row[3]))
                self.protein.append(float(row[4]))
                self.lipid.append(float(row[5]))
                self.carbohydrate.append(float(row[6]))
                self.calcium.append(float(row[7]))
                self.price.append(float(row[8]))
        self.num = len(self.name)


class Dish:
    def __init__(self, id, dish_types):
        self.figure_type=dish_types.type[id]
        self.fig=create_figure(dish_types.type[id],dish_types.size[id],dish_types.tray_size)
        self.name=dish_types.name[id]
        self.calorie=dish_types.calorie[id]
        self.protein=dish_types.protein[id]
        self.lipid=dish_types.lipid[id]
        self.carbohydrate=dish_types.carbohydrate[id]
        self.calcium=dish_types.calcium[id]
        self.price=dish_types.price[id]


class IndividualTray(list):
    """Container of a individual."""
    fitness = None
    img = None

    def __new__(cls, a):
        return list.__new__(cls, a)


def create_figure(fig_type, size, tray_size):
    x = random.randrange(size, tray_size[0] - size)
    y = random.randrange(size, tray_size[1] - size)
    if fig_type == 0:
        return matplotlib.patches.Rectangle((x, y), size, size, facecolor='black', alpha=0.5)
    if fig_type == 1:
        return matplotlib.patches.Circle((x, y), radius=size, facecolor='black', alpha=0.5)


def create_ind(n_gene, dish_types):
    """Create a individual."""
    return IndividualTray([Dish(random.randint(0, dish_types.num-1), dish_types) for i in range(n_gene)])


def create_pop(n_ind, n_gene, dish_types):
    """Create a population."""
    pop = []
    for i in range(n_ind):
        ind = create_ind(n_gene, dish_types)
        pop.append(ind)
    return pop


def set_fitness(eval_func, pop):
    """Set fitnesses of each individual in a population."""
    for i, fit in zip(range(len(pop)), map(eval_func, pop)):
        pop[i].fitness = fit


def evalOneMax(ind):
    """Objective function."""
    evaluation = 0
    for dish in ind:
        evaluation += dish.calorie
    # TODO:グラフ領域を設定

    #TODO:いい感じにトレーのサイズを外から引数として撮ってくるやり方がわからない直接書いた

    tray_size = np.array([370, 260])
    fig = plt.figure()
    axis = fig.add_subplot(1, 1, 1, xlim=tray_size[0], ylim=tray_size[1], aspect='equal')
    for dish in ind:
        axis.add_patch(dish.fig)
    axis.axis("off")
    plt.savefig('tmp.png')
    fig.delaxes(axis)
    plt.clf()
    plt.cla()
    plt.close()
    del axis
    gray_img = cv2.imread('tmp.png')
    ind.img = gray_img
    ret, dishes_area_image = cv2.threshold(gray_img, 200, 255, cv2.THRESH_BINARY)#図形がある
    dishes_area = cv2.countNonZero(cv2.bitwise_not(cv2.cvtColor(dishes_area_image, cv2.COLOR_RGB2GRAY)))
    cv2.imwrite('dishes_area.png',dishes_area_image)
    ret, dishes_overlap_area_image = cv2.threshold(gray_img, 100, 255, cv2.THRESH_BINARY)#重なってて黒い
    cv2.imwrite('dishes_overlap_area.png',dishes_overlap_area_image)
    dishes_overlap_area = cv2.countNonZero(cv2.bitwise_not(cv2.cvtColor(dishes_overlap_area_image, cv2.COLOR_RGB2GRAY)))
    overlap_per = 1 - dishes_overlap_area/dishes_area

    return evaluation * overlap_per


def selTournament(pop, n_ind, tournsize):
    """Selection function."""
    chosen = []
    for i in range(n_ind):
        aspirants = [random.choice(pop) for j in range(tournsize)]#適当にtournsizeこだけ個体を選ぶ
        chosen.append(max(aspirants, key=attrgetter("fitness")))#fitness属性の一番大きいものを選ぶ
    return chosen


def cxTwoPointCopy(ind1, ind2):
    """Crossover function."""
    size1 = len(ind1)
    size2 = len(ind2)
    tmp1 = ind1.copy()
    tmp2 = ind2.copy()
    cxpoint1 = int(size1/2)
    cxpoint2 = int(size2/2)
    tmp1, tmp2 = tmp2[:cxpoint2].copy()+tmp1[cxpoint1:].copy(), tmp1[:cxpoint1].copy()+tmp2[cxpoint2:].copy()
    return tmp1, tmp2


def mutFlipBit(dish_types, ind, indpb):
    """Mutation function."""
    tmp = ind.copy()
    for i in range(len(ind)):
        if random.random() < indpb:
            rand = random.randint(0, 2)
            # 増やす
            if rand == 0:
                tmp[i] = ind.append(Dish(random.randint(0, dish_types.num-1), dish_types))
                break
            # へらす
            if rand == 1:
                tmp[i] = ind.pop(random.randint(0,len(ind)-1))
                break
            # 変える
            if rand == 2:
                randopos = random.randint(0, len(ind) - 1)
                tmp[i] = ind.pop(randopos)
                tmp[i] = ind.insert(randopos, random.randint(0, len(ind) - 1))
                break
    return tmp


def fig_save(ind, file_name):
    cv2.imwrite(',/img/'+file_name+'.png', ind.img)

if __name__ == "__main__":
    main()
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正の依頼

  • RyosukeSHIBATA

    2020/08/03 14:35

    ```
    File "/Users/hoge/Desktop/hoge/fig_ga.py", line 161, in evalOneMax
    axis.add_patch(dish.fig)
    File "/usr/local/lib/python3.7/site-packages/matplotlib/axes/_base.py", line 1916, in add_patch
    self._set_artist_props(p)
    File "/usr/local/lib/python3.7/site-packages/matplotlib/axes/_base.py", line 905, in _set_artist_props
    a.set_figure(self.figure)
    File "/usr/local/lib/python3.7/site-packages/matplotlib/artist.py", line 712, in set_figure
    raise RuntimeError("Can not put single artist in "
    RuntimeError: Can not put single artist in more than one figure
    ```

    キャンセル

  • RyosukeSHIBATA

    2020/08/03 14:36

    エラーはこんな感じでした

    キャンセル

  • meg_

    2020/08/03 17:09

    ・質問のプログラムは複雑そうですが、質問者さんご自身が作成されたものですか?
    ・問題を簡単にするためにもっと簡単なグラフから試された方が良いかと思います。(参考サイトのコードは動かしてみましたか?)

    キャンセル

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る