とあるメーカーエンジニアの趣味備忘録

Python, マイコンいじり, 日々の呟きなど

クラス 'numpy.bool_' に騙された話

今日も今日とてPython の些細なところに丁寧に躓いていく.

今回は,「'bool'型って1種類じゃないの!?」っていう話である.

そう,1種類ではなかったのである.

標準クラスの'bool'型と,numpyにおいて定義されている’bool’型(正確には 'numpy.bool_' 型)の2種類が存在する.

そのため, 'numpy.bool_' 型の 'False' を 次のように条件判定しても意味がないのである.

import numpy as np

data = np.array([1, 2, -1])

result = data[data > 0].any()
# data[data > 0] = [True, True, False]
# .any() は呼び出し元の配列型インスタンスの要素内に一つでも True が含まれていた場合にTrueを返す.
# この場合は,Trueが返却される.

if result is True:    # numpy.bool 型オブジェクトとbool型オブジェクトの一致判定をしているので,当然一致しているという判定はされない.
    print(True)
else:
    print(False)

出力結果

False

このようにして条件判定させたいときには,キャストを行うことによって 'numpy.bool_' 型を 'bool' 型に変換する必要がある.

PandasのDataFrameに対してiterrows()メソッドで一つずつ要素を呼び出して,ラベルが存在するかどうかを確かめようとしてエラーに引っかかった話

題名を読んで,わかっている人からすると「何を当たり前のことを書いているんだ」と言われてしまいそうだが,自分がこれで結構引っかかったので一応メモ.

先ず,pandas の DataFrame 配列において

あるラベル名のカラムが存在するのかどうかを確かめたいという時がある.

そんな時には,DataFrame配列の有しているcolumnsという変数を参照することによってその確認を行うことができる.

まず,次の DataFrame配列を用意する.

import pandas as pd
test = [
    ["Taro", "Tokyo"],
    ["Jiro", "Osaka"],
    ["Sinya", "Aichi"],
    ["Haruki", "Hokkaido"]
]
index = ["user", "address"]
# DataFrame
testpd = pd.DataFrame(test, columns = index)
testpd

f:id:makutsueeken5:20190218151354j:plain

そして,columnsという変数を参照して,特定のラベル名のカラムが存在するかどうかを確かめることができる.

if "address" in testpd.columns:
    print(True)
else:
    print(False)

出力結果

True

また,DataFrame配列の中身を一つ一つ取り出したくなる時がある.

そんな時には,DataFrame配列の有するメソッドであるiterrows()を用いることによって実現することができる.

ここからが本題であるが,iterrows()を用いて一つ一つ取り出したアイテムに対して,columnsという変数を参照して,特定のラベル名のカラムが存在するかどうかを確かめようとすると,

for index, item in testpd.iterrows():
    # index は 'int' type
    # item は 'pandas.Series' type
    print("************************************")
    print(item)
    # 'pandas.Series' type に対して,DataFrame型の時と同様に ".columns" を呼び出してみる
    if "address" in item.columns:
        print(True)
    else:
        print(False)

出力結果

True
************************************
user        Taro
address    Tokyo
Name: 0, dtype: object
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-27-555c8ebbccf2> in <module>()
      8     # 'pandas.Series' type に対して,DataFrame型の時と同様に ".columns" を呼び出してみる
      9     try:
---> 10         if "address" in item.columns:
     11             print(True)
     12         else:

~\Anaconda3\lib\site-packages\pandas\core\generic.py in __getattr__(self, name)
   4366         if (name in self._internal_names_set or name in self._metadata or
   4367                 name in self._accessors):
-> 4368             return object.__getattribute__(self, name)
   4369         else:
   4370             if self._info_axis._can_hold_identifiers_and_holds_name(name):

AttributeError: 'Series' object has no attribute 'columns'

このようにAttributeErrorを食らってしまう.

というのも,公式リファレンスを参照したところ

pandas.DataFrame.iterrows — pandas 0.24.1 documentation

iterrows()メソッドによって渡される値は'DataFrame'オブジェクトではなく,'Series'オブジェクトとして渡されるためである.

Seriesオブジェクトは,columnの無いDataFrameオブジェクト・一次元のDataFrameオブジェクトのようなものであるため,columnsを参照することができないのである.

その代わりに,Seriesオブジェクトはその行(index)のラベルを.keys()メソッドによって参照することができるのである.

for index, item in testpd.iterrows():
    # index は 'int' type
    # item は 'pandas.Series' type
    print("************************************")
    print(item)
    # 'pandas.Series' type に対して メソッド ".keys()" を呼び出してみる
    if "address" in item.keys():
        print(True)
    else:
        print(False)

出力結果

************************************
user        Taro
address    Tokyo
Name: 0, dtype: object
True
************************************
user        Jiro
address    Osaka
Name: 1, dtype: object
True
************************************
user       Sinya
address    Aichi
Name: 2, dtype: object
True
************************************
user         Haruki
address    Hokkaido
Name: 3, dtype: object
True

このようにして,indexを参照することができた.

並行処理を実現するアイディアを実装してみた.- Idea for data persistence in Pararell Processing

並行処理を実現するアイディアを実装してみた.- Idea for data persistence in Pararell Processing

未だに本ブログにおいて自分が作成したメインプログラムの解説を行っていないのだが(笑)

私はNYTimesの記事をスクレイピングするプログラムを作成した.本日はそれにまつわる内容だ(笑)

で,NYTimesは1996年?以降の記事に関しては積極的にオンライン掲載を行ってきたのであるが,それ以前の記事はアーカイブとして管理されている.

アーカイブの記事のうち,1960年代とか割と最近の記事に関してはその内容をデジタルテキストとして同様に掲載してくれている.

だが,それ以前の記事に関してはPDF画像として公開しているものが多い.

そのため,PDF画像として公開している記事の文章を取得したい場合には一筋縄では行かない.

そこで,私はOCRを用いてテキストデータに変換して取得するという方針にして,そのプログラムを作成したのである.

その概念図は次のようになっており,番号が振られているように3つの過程で分割され,それぞれの過程の処理を行うためのメインプログラム?的なものをそれぞれ作成した.

f:id:makutsueeken5:20190216143110j:plain

作成したプログラムを実行してみたところ,約5年分のデータに対して

ステップ1の実行速度が5時間だとすると,

ステップ2の実行速度は約1日から1日半程度

ステップ3の実行速度は約2週間程度

となっており,仕方ないことだがステップ3にとてつもなく時間がかかる.

この問題を何とかするために,次の2つの方法を思いついた.

  1. ステップ1を実行している最中に,別のターミナルでステップ2やステップ3を実行して,若干逐次的なイメージでデータを処理していく.
  2. 最終的にステップ2が終了して,ステップ3のみが残っているという状態に陥る.その時に,複数のターミナルでステップ3を実行する.要するに,同時に3つぐらいのファイルにOCRの処理をかけているという状態を実現する.

だが,これをやっていく上で問題になってくるのがpersistenceやログ的な問題である.

あるターミナルでステップ1の処理のループの一つを終えて,一旦データファイルを保存して(更新して)から次のループを処理しているとする.その直後に同様に一部のループ処理を終えたステップ2の方でデータファイルを保存して更新したとする.

このステップ2の更新後のデータファイルには,きちんとステップ1の処理による更新内容が引き継がれていなければならないのである.

こういった類の問題のことをどのような名前で読んでいるのかがわからないのでくどくどと長い説明になってしまったが,今回はこの対策案について考えた結果をここに記す.

DataFrameを使うようにプログラムを改修大作戦

makutsueeken5.hatenablog.com

この記事にも書いていたように,

パーシステンスの実現のためにこれまではlistオブジェクトをpickleファイルとして保存して,という方法をとっていた.

しかし,この方法は何とも「車輪の再開発」感があふれる方法であった.というのも,リストを読み込んでソートしてサーチするためのプログラムを作成したり, リストを分割するためのプログラムを作ったりという手間がかかっていたからだ.まあ,ある意味勉強になった部分もあったが,何とも運用コストが高すぎる方法論であった.

それに,どっちみち今後何らかの形でデータベースを扱う機会が出てくるであろうから,データベースを扱ういいチャンスだと思って,Pandasに手を出してみることにした.

ということで,Pandas の DataFrame を用いて,これまでlistで扱っていたデータをDataFrameに移行していく.

だから,この記事は誰かに読んでもらうためというよりかは,自分の進捗をメモしておくための進捗ログのようなものである.

以下ログ


  • 2/20

Digitalized_article_collecting.py

改善終了

一応これで全てのメインプログラムの改修作業が終了した.

Digitalized_article_collectingとGetting_pdfpagelink_service.pyを結合するとかしないとかの話はまた余力のある時に.

<展望>

GUI

・pyinstallとかいうパッケージを使うことによって実行ファイル化することができる.

  • 2/16

Dataframe_handle_service.py

追加

Converting_pdftotext_service.py

改善終了

Download_pdfarticle_service.py

改善終了

参考文献

qiita.com

tonari-it.com

  • 2/15

Url_handler.py

Url_handler.pyの改善終了.

Getting_pdfpagelink_service.pyも改善終了.

因みに,この時点でのデータは

f:id:makutsueeken5:20190215143139j:plain

そして,ディレクトリ構造は,

--- PDFver_articles_collecting
    |
    ------ INFO_DATA (記事のテキストデータ以外で扱うデータファイルを保存)
            |
            ------ DATA_LIST (ダウンロード記事のurlリスト等を保存)
                    |
                    ------ url_data_list_news_19710101-19650101.
            |
            ------ CHROMEDRIVER_USERDATA_DLPDF (Google Chrome のユーザーデータを保存)
    |
    ------ src (ソースコードを格納)
    |
    ------ DATA (記事のテキストデータを保存)
 

PandasのDataFrameについての備忘録

PandasのDataFrameについての備忘録

パーシステンスの実現のためにこれまではlistオブジェクトをpickleファイルとして保存して,という方法をとっていた.

しかし,この方法は何とも「車輪の再開発」感があふれる方法であった.というのも,リストを読み込んでソートしてサーチするためのプログラムを作成したり, リストを分割するためのプログラムを作ったりという手間がかかっていたからだ.まあ,ある意味勉強になった部分もあったが,何とも運用コストが高すぎる方法論であった.

それに,どっちみち今後何らかの形でデータベースを扱う機会が出てくるであろうから,データベースを扱ういいチャンスだと思って,Pandasに手を出してみることにした.

だが,なかなか一筋縄ではいかない.確かに速いし便利なことはわかるのだが,なかなかのひねくれものでもあるといった所感でもある. そこで,備忘録を作っておくことにした.随時追加していく予定.

作成したDataFrameオブジェクトからの要素の抽出について.

import pandas as pd
test = "hatenauser", "password"
index = ["user", "pass"]
testpd = pd.DataFrame(test, columns = index)
print(list(testpd))
print(list(testpd["user"]))
print(list(testpd.iloc[0,:]))
                    

出力結果

: ['user', 'pass']
: ['hatenauser']
: ['hatenauser', 'password']
            

DataFrameオブジェクトへ要素の追加をする方法

test = [["ha", "te", "hl"],["te", "na", "lk"]]
testpd = pd.DataFrame(test, columns=["date", "url", "appendix"])
testpd              

出力結果

f:id:makutsueeken5:20190214095304j:plain

addtest = [["ha", "hl"],["na", "lk"]]
addtestpd =pd.DataFrame(addtest, columns=["url", "appendix"])
testpd = testpd.append(addtestpd, ignore_index=True) # データフレーム の結合 (元の行番号を無視)
testpd             

出力結果

f:id:makutsueeken5:20190214095225j:plain

このように,.appendメソッドを用いることによって リストを結合するかのようにデータフレームに行を追加することができる.

また,.appendメソッドのオプションの引数をignore_index=Trueと設定することによって, 元のデータフレームの行番号を無視してリストの結合を行うことができる.

DataFrameに指定した名前のカラムが存在するのかどうかを確かめる方法.

こちらを参考にした. <Python, pandas> カラムがあるか? と カラムの追加の仕方。- ねこゆきのメモ

辞書型リストに指定したラベルが存在するかどうかを確かめるときに使う手法と基本的には同じなのであるが,DataFrameリストにおいては .columnsによってメンバ変数を指定して呼び出す必要があるようだ.

if "dldate" in dataframe.columns:
print("Exists")
else:
print("Not Found")
            

Python でパッケージやクラス間の依存関係を可視化してみた.[Pyreverse]

Python でも他のプログラミング言語でも,割と大きめのプロジェクトを作成している際に直面しやすい問題として次の問題があると感じる.  

「トップのクラスは一体どのクラスとどのクラスとどのパッケージを読み込んでいて,さらに,その読み込まれた元のクラスは一体どのクラスとどのクラスとどのパッケージを読み込んでいたんだろうか...」

そう,パッケージ間やクラス間の依存関係が分からなくなってしまうのである.
(チームで開発するときや仕事で開発するときなど,設計の時点でしっかりと概念図やクラス図等を作成していればそのようなことは回避できる!と言われたらぐうの音も出ないというやつだが.自分のような初心者&個人開発者にとっては,最初の方は同じディレクトリ下に次々とソースファイルを作って後から見たら,どれとどれがメインファイルで,どれがサブ的なファイルだろうか...みたいな状況になりかねない気がする.)

とりあえず,クラス間の依存関係をとにもかくにも整理したかったのである.
そこでググっていたら次のような記事がヒットした.

Python - Pythonでモジュールの依存関係図を描きたい|teratail

要するに,ここで質問している人も自分と同じような機能を実現したいと考えていて質問しており,
"pylint による pyreverse を使ったらイイよ!"
と言われているのである.

で,pyreverse を導入する手順について

linuxBean14.04(49)pyreverseでPythonコードからUML図を生成

上のリンクに説明されていたので,その通りに導入してみた.
因みに,私の環境は,Python3.6で実行環境はAnaconda Prompt(Windows 10)である.

先ず,pylint をインストールする.

conda install pylint

次に,pyreverse がきちんと動作するのかを確かめるために,次の公式ドキュメントに載っていたコードを実行してみた.

Pyreverse : UML Diagrams for Python (Logilab.org)

pyreverse -o png -p Pyreverse pylint/pyreverse/

すると,その実行結果が

The output format 'output.png' is currently not available. Please install 'Graphviz' to have other output formats than 'dot' or 'vcg'.

どうやら,"Graphviz"というパッケージがインストールされていないようなのである.
この問題に関しては,stackoverflowにQ&Aが投稿されていた.

python - Pyreverse complaining even after having Graphviz - Stack Overflow

要するに,インストールしたうえで,パスを通せばよいということなのである.
なので,

conda install graphviz

でインストールを行い,
パスを通した.
graphvizのパスは

C:\Program Files (x86)\Graphviz2.38\bin

であるので,これを環境変数PATHに追加した.
そして,実行すると又エラー
以下のリンクにヒントとなることが書いてあった.
Windows の Anaconda 環境で pyreverse がエラーになる場合の対策 | 南房総ツーリング日記

どうやら,対策としてPATH 環境変数Graphviz のパスを定義しておけば、サブプロセスにも PATH 環境変数が引き継がれる。その結果、サブプロセスでも dot コマンドを実行することができるようになる。
らしい.(windowsのパスとanacondaのパスは何か違うのかもしれない.)

set PATH=%PATH%;%GRAPHVIZ_DOT%\..

を実行して,anaconda promptを再起動して実行してみたら上手くいった.

そして,上にも載せたpyreverseの公式ドキュメントは英語で書かれているため,その内容に関して備忘録をここにまとめておく.

Introduction
Pyreverse builds a diagram representation of the source code with:
class attributes, if possible with their type
class methods
inheritance links between classes
association links between classes
representation of Exceptions and Interfaces

先ず,Introductionの項には,

Pyreverseはソースコードのダイアグラム図表現を出力します.その出力内容は,

  • クラス属性や型(public, privateやint, char等)

  • クラスメソッド

  • クラス間の継承関係

  • クラス間の関連性

  • 例外とインタフェースの表現

となっていると述べられている.

使い方として,

pyreverse -o [出力フォーマット] -p [出力ファイル名] [ターゲットファイル名・ターゲットディレクトリ名] 

を実行することで,次の2つのファイルが作成されるという.

  • Package Diagram

こちらは,packages_[指定したファイル名].[指定した出力拡張子]という形式でファイルが出力されるよう.

  • Class Diagram

こちらは,class_[指定したファイル名].[指定した出力拡張子]という形式でファイルが出力されるよう.

ちなみに,-oと-pはオプションであり,

  • -o [出力フォーマット] : -o png のような形式で指定する.

  • -p [出力ファイル名] : -p pytest のような形式で指定する.

実際に実行してみたところ,

pyreverse Urllist_editor.py

f:id:makutsueeken5:20190213141011j:plain

というファイルが生成された.

どうやらこれはword形式のファイルだそうで,開いてみると, f:id:makutsueeken5:20190213141103j:plain

という画面が出力されたが,いまいちよくわからない.

そこで,

pyreverse -o jpeg -p Urllist_editor Urllist_editor.py

を実行したところ,

f:id:makutsueeken5:20190213141248j:plain

が出力された. どうやら,細かなオプションを指定しないとそのクラスのクラス図とメソッドだけが出力されるようだ.

その他のオプションは以下の通り.

-a N, -A

サーチする親クラスの深さの度合い.小文字で指定する場合は,-a1, -a2のように深さを表す数を指定する. 大文字-Aで指定した場合には,すべての親クラスをサーチする.

-s N, -S    

サーチする関連クラスの深さ度合い.小文字で指定する場合には,-s1, -s2のように深さを表す数値を指定する. 大文字-Sで指定した場合には,すべての関連クラスをサーチする.

-A, -S      

上の大文字バージョンの説明.すべての親クラス,ないしは,関連クラスをサーチする.

-m[yn]      

モジュール名(Pathlibとかurllibとか)が表示されるかされないか.

されてもよい場合は-myと指定して.その結果として"Pathlib.Path"等のようにクラス名のところに表示される.

されてほしくない場合は-mnと指定して,その結果として"Path"のようにクラス名からモジュール名が表示されない形でダイアグラムが作成される.

-f MOD      filter the attributes : PUB_ONLY/SPECIAL/OTHER/ALL

表示する属性値を(メソッドの種類など)をフィルタリングする.

-f PUB_ONLY のようにモードを指定するみたい.

モードは4種類あるようで,

  • PUB_ONLY : パブリックメソッドだけを表示するみたい

  • SPECIAL

  • OTHER

  • ALL : 全ての属性値を表示するようだ.

-k          

属性値もメソッドも表示せずに,クラス名のみを表示していく.

-b          show 'builtin' objects

ビルトインオブジェクトのみを表示する.

pyreverse -ASmy -o png Urllist_editor.py -p Urllist_editor

を実行した結果その出力として次の結果が得られた. f:id:makutsueeken5:20190213144957p:plain

技術英語集

技術英語

  ここ3日間ぐらいずっとherokuと格闘し続けていた.
というのも,heroku の公式ドキュメントを英語で読破しようと試みていたためである.
ま~あなんとも難しい難しい.そもそもIT用語で日本語でも知らない言葉や概念がアホほど出てきていたためである.
なんか,半分くらいは英語であるという言語の壁で悪戦苦闘していた節があったが,残り半分くらいは単に技術を知らないためにそこの勉強不足という意味で悪戦苦闘していた.
ということで,以下は自分が初見であった英単語をまとめた自分メモみたいなものである.

Word(English)意味appendix
deploy

配置する,展開するという意味

そこから発展して,システムをリリースすることや,ステージングすることも含めて,あるシステムを利用可能な状態にすることを意味する.

aggregate を集計する.集める.
compatible with - ーと互換性がある.-と両立し得る.-と相性がいい.
convention 規則,しきたり
traffic : トラフィック Webの分野では、WebサーバやWebサイトへの外部からの接続要求数、アクセス数、送信データ量などのことや、サイトやページの間を行き来する閲覧者の流れのことをトラフィックということが多い。
query

クエリー(Query)とは、質問、問い合わせ、照会という意味の英単語で、データベースの分野では、条件に合致するレコードの検索・抽出、あるいは置換、更新、削除などの操作を行うための命令・要求のことである。

データベースでクエリーを扱うための言語は問い合わせ言語という。

リレーショナルデータベースにおいては、SQL(Structured QueryLanguage)という言語が標準的に使われており、これを使って命令・要求を記述する。またXMLデータベースにおいては、XQueryという言語が標準的である。

assert たぶんresourcesの意味でつかわれている.
propagate 伝搬する.伝える.
out-of-the-box

先入観や既成概念から離れて

創造的な、独創的な、形にとらわれない、従来の常識を破る、革新的な

persistence

オブジェクトやシステムの状態などを、プロセスやセッション、マシンなどの物理的、時間的境界を越えて維持、保存する機能のこと。persistenceとは、「持続、永続、保持」などという意味。

プログラムがメモリ上に保持している変数やデータなどは、プログラムが終了すると失われ、再度起動しても同じ状態に戻ることはない。だがこれでは、例えばユーザーが入力したデータや設定などが失われてしまい、後で作業を継続することができない。そこで、再度起動しても以前と同じ状態を回復できると便利である。これを実現する機能のことを永続性(パーシステンス)という。

永続性を実現するにはいろいろな方法があるが、プログラムの終了時に「状態を保存」しておいて、次回の起動時に「状態をロード」するという方法が一般的である。このような機能は非常に基本的なものなので、ライブラリにおいて実装されていることが多い。また、データの格納やアクセスをデータベースに対して行い、データベースを使って永続性を実現するという方法もある。

このほかにも永続性は、Webサイトにおけるセッションの同一性管理などでも利用されている。一般にWebサイトには複数のWebサーバ・マシンが存在し、それぞれのサーバの内部には多くのプロセスやスレッドが存在している。ユーザーがWebサイトに接続するときに使われるHTTPプロトコルでは、どのサーバのどのスレッドにするかは、HTTPのGET要求ごとに毎回動的に変わる可能性がある。だが同じユーザーからの一連の接続要求であることを認識していないと、例えば買い物バスケットの内容が消えてしまったり、他のユーザーのものと混同してしまったりする。

このような事態を防ぐには、URLやHTTP,CookieIPアドレス、GETコマンドのパラメータなどから同一のセッションであることを判断し、処理する必要がある。このように、HTTPセッションの同一性を保証することを永続性処理(パーシステンス処理)などという。

provision (サーバなどを)使えるように設定する.
abuse 不正使用や乱用を意味する.
REPL (Real-Eval-Print-Roop)

入力・評価・出力のループ

主にインタプリタ言語においてコンソールで対話式でコードを実行する仕組み.

ephemeral つかの間の,一時的な = temporary
populate A with B (データベースなどにデータを)追加する,投入する.

populate a database with data

データベースにデータを追加する.

Along with ~ ~するにつれて

Along with the spread of the Internet, ~

インターネットの普及につれて

open the website ウェブサイトを開設する
dedicated 専用の