【Python】listやndarrayの中身をエクセルに張り付けるためにクリップボードへコピー
listやndarrayの中身をエクセルに張り付けて検証や検討をしたい場合に有効。
肝になるのが
・リスト内容標記を使って数値を文字列にして末尾に改行コード\nを追加
・"".join(データ)で文字を結合
import pyperclip def copy(data): temp = [str(i) + "\n" for i in data] pyperclip.copy("".join(temp)) print("data was copied in clipboard")
クリップボードコピー用のモジュール、pyperclipを使って関数化し
リストからクリップボードコピーまで1行で処理可能にした。
【Python】グラフ枠(Spines)の色・太さ変更
グラフ枠(Spines)を一度描画した後に変更する方法。
グラフ枠は
ax.Spines{"Bottom"}
ax.Spines{"Top"}
ax.Spines{"Left"}
ax.Spines{"Right"}
に枠線のインスタンスがあり,
色変更は ax.Spines{"**"}.set_color(色)
太さ変更は ax.Spines{"**"}.set_linewidth(太さ)
で変更する。
問題は枠線(Spines)がAXESエリアのぎりぎり外側にいるため,
背景を保存する際や再描画エリアを指定する際に使う
ax.bboxでは範囲に含まれないこと。
解決方法として
① ax.bbox.extended(縦?倍率,横?倍率)でax.bboxの範囲を少し広げてやる。
問題点はAxesサイズが変わると拡張される面積も変わるためプログラム毎で調整が必要になってしまう。
② ax.xaxis.get_tightbbox(ax.get_renderer_cache())でtickラベルを含むaxisのbboxを取得し,
ax.bbox.union([結合したいbbox, 結合したいbbox])でbboxをつなげる。
注意点はax.xaxis.get_tightbbox()がほんとにtightでtickラベルぎりぎりのbboxを返す。
そのためtickラベルの桁数が変わる場合など簡単にエリアをはみ出すので注意。
グラフをダブルクリックしてグラフ枠を赤&太く -> 黒&細くを繰り返す。
def event_datacheck(self, event): if event.dblclick: if event.inaxes.spines["bottom"].get_edgecolor()[0] == 0: # 赤線を引く前にbgを保存 # キーをイベントのaxesインスタンスにしてクリックされたaxesにbgを引き当て # key:axインスタンス value:bg self.ax_dict[event.inaxes] = event.canvas.copy_from_bbox(event.inaxes.bbox.expanded(1.1, 1.1)) event.inaxes.spines["bottom"].set_color("red") event.inaxes.spines["top"].set_color("red") event.inaxes.spines["left"].set_color("red") event.inaxes.spines["right"].set_color("red") event.inaxes.spines["bottom"].set_linewidth(2) event.inaxes.spines["top"].set_linewidth(2) event.inaxes.spines["left"].set_linewidth(2) event.inaxes.spines["right"].set_linewidth(2) print("Axes was SELECTED") else: # イベント内のaxesに対応するbgを使って画面をbgに戻す event.canvas.restore_region(self.ax_dict[event.inaxes]) event.inaxes.spines["bottom"].set_color("black") event.inaxes.spines["top"].set_color("black") event.inaxes.spines["left"].set_color("black") event.inaxes.spines["right"].set_color("black") event.inaxes.spines["bottom"].set_linewidth(0.5) event.inaxes.spines["top"].set_linewidth(0.5) event.inaxes.spines["left"].set_linewidth(0.5) event.inaxes.spines["right"].set_linewidth(0.5) print("Axes was RELEASED") event.inaxes.draw_artist(event.inaxes.spines["bottom"]) event.inaxes.draw_artist(event.inaxes.spines["top"]) event.inaxes.draw_artist(event.inaxes.spines["left"]) event.inaxes.draw_artist(event.inaxes.spines["right"]) # これで更新 event.inaxes.figure.canvas.blit(event.inaxes.bbox.expanded(1.2, 1.2)) # event.inaxes.figure.canvas.update() <- wxaggだと動かない
【Python】matplotlibの手動で描画更新
matplotlibでアニメーションほど早い更新周期ではないものの,
イベント(たとえばグラフをダブルクリック)でグラフを更新(変化)させたい場合など,
グラフ更新の関数を実行してあげる必要がある。
最も一般的なのは
canvas.draw()
しかしこれがすごく遅い。
Figureクラスの中の関数のためかfigure全体を更新してしまうためらしい。
色々試した結果,
① bg = canvas.copy_from_bbox(self.ax.bbox) # bboxのエリアを背景(というか戻したい状態)として保存
② canvas.restore_region(bg) # 戻したい状態へ描画
③ ax.draw_artist(artist) # 描画したいartist(LineやPatch)を登録
④ canvas.blit(ax.bbox) # bboxのエリアだけ描画更新 以降②へ戻る
※ ④ canvas.blitよりcanvas.update()の方がメモリ使用が少なく早い情報もあるが
ケースによるのとqt4aggでしか動かなかった。
でやると更新したい場所のみ変更するのでmatplotlibとは思えないほど早い。
bboxのエリアを前に保存した状態に戻す手順とその効果はペイントみたいに感じる。
参考にしたサイトにあったcanvas.draw()と他のもっと早い手順との速度比較を試してみた。
import matplotlib.pyplot as plt import numpy as np import time from scipy.stats._continuous_distns import t_gen def case1(): fig, ax = plt.subplots() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: ax.clear() ax.plot(np.random.randn(100)) plt.pause(0.001) num_plot += 1 return num_plot def case2(): fig, ax = plt.subplots() line, = ax.plot(np.random.randn(100)) t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) plt.pause(0.001) num_plot += 1 return num_plot def case3(): fig, ax = plt.subplots() line, = ax.plot(np.random.randn(100)) fig.canvas.draw() fig.show() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) fig.canvas.draw() fig.canvas.flush_events() # <-これがないと画面に描画されない。 num_plot += 1 return num_plot def case4(): fig, ax = plt.subplots() line, = ax.plot(np.random.randn(100)) fig.canvas.draw() fig.show() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) ax.draw_artist(ax.patch) ax.draw_artist(line) #fig.canvas.update() fig.canvas.blit(ax.bbox) fig.canvas.flush_events() num_plot += 1 return num_plot def case5(): fig, ax = plt.subplots() fig.canvas.draw() bg = fig.canvas.copy_from_bbox(ax.bbox) line, = ax.plot(np.random.randn(100)) fig.show() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) fig.canvas.restore_region(bg) ax.draw_artist(line) #fig.canvas.update() fig.canvas.blit(ax.bbox) fig.canvas.flush_events() num_plot += 1 return num_plot def case6(): fig, ax = plt.subplots() fig.canvas.draw() bg = fig.canvas.copy_from_bbox(ax.bbox) line, = ax.plot(np.random.randn(100)) fig.show() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) fig.canvas.restore_region(bg) ax.draw_artist(line) fig.canvas.update() fig.canvas.flush_events() num_plot += 1 return num_plot if __name__ == "__main__": print("case1: " + str(case1()) + "fps") print("case1: " + str(case2()) + "fps") print("case3: " + str(case3()) + "fps") print("case4: " + str(case4()) + "fps") print("case5: " + str(case5()) + "fps") print("case6: " + str(case6()) + "fps")
Case1: 最も原始的。Lineを書いては消して。Pauseを使うことで画面を更新。
Case2: Case1からLineをいちいち消すのではなくLine1のデータを書き換えて更新
Case3: Case2 + canvas.draw() これが基準になる
Case4: 背景を白で描画する(ax.draw_artist(ax.patch))ことで前の描画をクリア。再描画はcanvas.blit()を使う
Case5: Case4から前描画クリアをcanvas.restore_region()に変更
Case6: Case5から再描画をcanvas.update()に変更
いろんなケースを試したけど要は
・Case3基準に対してCase5がどれだけ速いか。
・Case5とCase6どっちが速いか。
結果は
case1: 5fps
case1: 7fps
case3: 44fps
case4: 227fps
case5: 218fps
case6: 183fps
Case3に対してCase5は約5倍速い。
Case5よりCase6の方が約1.2倍速い結果となった。
意外にCase4が遅くない。。
【Python】Pycharm + wx3 でエラー
PycharmのPython Console使用時に発生したエラー。
おそらくbackednをpyqt4からwxに変更したことも関係あると思われる。
C:\Program Files (x86)\JetBrains\PyCharm Community Edition 2017.1\helpers\pydev\pydev_ipython\inputhookwx.py
def inputhook_wx3(): """Run the wx event loop by processing pending events only. This is like inputhook_wx1, but it keeps processing pending events until stdin is ready. After processing all pending events, a call to time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%. This sleep time should be tuned though for best performance. """ # We need to protect against a user pressing Control-C when IPython is # idle and this is running. We trap KeyboardInterrupt and pass. try: app = wx.GetApp() # @UndefinedVariable if app is not None: assert wx.IsMainThread() # @UndefinedVariable # The import of wx on Linux sets the handler for signal.SIGINT # to 0. This is a bug in wx or gtk. We fix by just setting it # back to the Python default. if not callable(signal.getsignal(signal.SIGINT)): signal.signal(signal.SIGINT, signal.default_int_handler) evtloop = wx.GUIEventLoop() # @UndefinedVariable ea = wx.EventLoopActivator(evtloop) # @UndefinedVariable t = clock() while not stdin_ready(): while evtloop.Pending(): t = clock() evtloop.Dispatch() #app.ProcessIdle() wx.WakeUpIdle()
wx3になって
wx.Thread_IsMain() が wx.IsMainThread に変更されているのに
プログラム内ではwx2のままが原因。
ほかにも
wx.EventLoop() が wx.GUIEventLoop() を使うよう推奨されていたので変更した。
【Python】Pycharmアップデート後にPythonクラッシュ多発
Pycharmはずっと2016.3バージョンを使っていたが知らぬ間に2017.1という新バージョンに。
機能もデバッグの高速化とPandasの可視化と魅力的だったため
アップデートしたところ,前は普通に動いていたプログラムが立て続けに
Pythonのcrashで動かず。。
その時のエラーは
コンソールに
Process finished with exit code -1073741819 (0xC0000005)
と
"Pythonは動作を停止しました"
というウィンドウ。
色々原因を調べていくとmatplotlibをインポートするところで
エラーが発生。
インストールされていたmatplotlibのバージョンが1.*で
2.0.0が新たに公開されていたので試しに更新したところ直った!