プログラミングのネタ帳

30代からプログラミングをはじめた。記憶力が悪いのでメモ代わりに。

pythonでtimeモジュールを使って実行時間を調べる

pythonには日時時間に関する、timeと名の付くに関するモジュールが2つあります。

ややこしい。 (しかも、datetimeモジュールにtimeクラスがあったりする)

・datetime モジュール

・time モジュール

datetimeは日時を扱うときに使います。timeモジュールも機能は色々ありますが、datetimeモジュールでよくね?となり、あまり使用する機会はない、というのをどこかで目にした気がします(伝聞系)

表題のように実行時間を調べるのはtimeモジュールを使うと簡単にできます。


    import time 
    
    start=time.time()
    print("Hello World!")
    elaps=time.time()-start
    print(elaps)

実行結果
Hello World!
elaps= 5.1975250244140625e-05

ある関数の実行時間を調べる関数(measure_time(func))を作ってみました。

 


import time

def measure_time(func):
    """measure_time(func)
    機能
        渡した関数(func)を実行し、その実行時間を計測、表示する
    引数 (func) : 任意の実行する関数。
    戻り値 なし
    例外 なし
    使用例 measure_time(myfucn)
    """
    start=time.time()
    func()
    elaps=time.time()-start
    print(elaps)

def say_hello():
    print("hello world san!")

if __name__=="__main__":
    measure_time(say_hello)

mainでmeasure_timeを実行するさい、引数であたえる関数は、()をつけないことに注意が必要です。()をつけると、そのタイミングで実行してしまいます。

では、引数が必要な関数はどうしたらいいでのしょうか?

どうしらたいいのでしょうね。わかりません。とりあえずは、以下のように実行する関数と同じように引数で与えてしまう手段はあるのかなと思います。


import time

def measure_time(func):
    """measure_time(func)
    機能
        渡した関数(func)を実行し、その実行時間を計測、表示する
    引数 (func) : 任意の実行する関数。
    戻り値 なし
    例外 measure_time(myfucn)
    使用例
    """
    start=time.time()
    func()
    elaps=time.time()-start
    print(elaps)

def measure_time_with_an_arg(func,arg):
    """measure_time(func,arg)
    機能
        渡した関数(func)を実行し、その実行時間を計測、表示する
    引数 (func,arg) , func:任意の実行する関数。arg:実行する関数の第一引数
    戻り値 なし
    例外 
    使用例 measure_time(myfucn,myarg)
    """
    start=time.time()
    func(arg)
    elaps=time.time()-start
    print(elaps)

def say_hello(who):
    print("hello world",who,"san!")

if __name__=="__main__":
    measure_time_with_an_arg(say_hello,"foo")

pythonのdocstring機能を使って関数の仕様を書く・確認する

少しずつ読み進めているオライリーの実践Cプログラミングでは、コメントを書くこと、

例えば、変数名に関して、

・意味のある名前にして、極力省略しない。

・宣言は1つにつき1行使う。コメントをつけて、単位を書いておくのが好ましい

という風に徹底しています。

関数に関しても、

・引数、戻り値をきちんと明示する(型、等々)

ということが書いてあった気がします。

 

そこで表題、pythonではdocstringという関数の仕様を自分で記入できる機能があり、また標準関数についてもその仕様を確認することができます。

また、標準関数についてもdocstingを参照し使い方を知ることができます。

docstringの表示は、以下の通り、特殊メソッド__doc__、またはhelp関数を使う方法があります。

・funcname.__doc__

・help(funcname)


print(list.insert.__doc__)
print(help(list.insert))
実行結果
Insert object before index.

Help on method_descriptor:

insert(self, index, object, /)
    Insert object before index.

print関数を使っていますし、行が少ないので分からないですが、インタラクティブでは__doc__は改行せずに表示されます。 help関数は整えて表示します。

自作の関数にdocstringを記述するには、関数名の下の行に""" """ と囲むことで実装出来ます。 書き方は色々あると思います。以下の例では関数の機能。引数。戻り値・例外処理・使用例を書いています。


def mydivision(a,b):
    """
    関数名 mydivision(a,b)
    機能
        第一引数を分子 第二引数を分母として
        割り算を計算し答えを返す
    引数 (float a,float b)
     float a 分子
     float b 分母
    戻り値 float: 1/b
    例外 b=0であればzerodivisionerrorとして検出しsys.exit()する
    例 myadd(10,2)
    """
    a,b=float(a),float(b)
    try:
        return a/b
    except ZeroDivisionError as e:
        print(e,"第二引数は分母で、0を代入しています")
        sys.exit()

help(mydivision)とすれば、以下が表示されます


Help on function mydivision in module __main__:

mydivision(a, b)
    関数名 mydivision(a,b)
    機能
        第一引数を分子 第二引数を分母として
        割り算を計算し答えを返す
    引数 (float a,float b)
    戻り値 float: 1/b
    例外 b=0であればzerodivisionerrorとして検出しsys.exit()する
    例 myadd(10,2)

関数の使い方を忘れたのなら、ググるよりもまず関数のdocstringを確認する癖をつけた方がいいかもしれないと最近思っています。

record_type='3d'と指定し、3次元座標情報をもつcompoundオブジェクトを生成する

前回からの続き

 

myprograming-tips.hatenablog.jp

pcp.Compound.from_cid()メソッドの引数で,record_type='3d'とすると3次元の座標データをもつcompoundオブジェクトを得られます


pcp.
    

 

しかし、注意点として、3dとして入手したcompoundオブジェクトは、大部分のプロパティを使うことが出来ない様子です。(例えば、cmp_obj_3d.iupac_nameとしてもNoneを返す)

そのため、関数としては、record_type='2d', '3d'の両方をオブジェクトを返すようにしたほうが後々便利だと思います。


#関数名 get_cmp_object(str) 関数
#機能
#  引数で受けたcidからcompoundオブジェクトを生成、
#  record_type=3dとして3d情報をもったcmp_objはプロパティを表示しない
#  そのため、3d,2dの両方を返す
#引数(str) 1:cid
#戻り値(compound_object,compound_object) 1:compoundオブジェクト3d ,2d
def get_cmp_object(mycid):
    cmp_3d=pcp.Compound.from_cid(mycid,record_type='3d')    #!!!3dではプロパティを得られない!!!
    cmp_2d=pcp.Compound.from_cid(mycid,record_type='2d')
    
    return [cmp_3d,cmp_2d]

pythonは戻り値をいくつでも簡単に設定できるのでいいですよね

次回から座標データや結合データを用いて.xyz や.mol2ファイルを作りたいと思います。

pubchem でxyz形式のファイルを作成する

pubchempyで対象の分子のcomformer オブジェクトがあり、かつ3dのデータがあるのであれば、xyzファイルを書き出して、

xyz形式は

1行目:原子の数

2:行目空白行

3行目以降:原子記号_or_id x座標 y座標 z座標

・・・

 

3dデータをもつcomformerオブジェクトである必要があります。

それ入手は以下の記事を参照してください。

 

 

myprograming-tips.hatenablog.jp

 

 

 


#関数名 make_xyzfile(compound_obj_3d,compound_obj_2d)
#機能
#  cmp_objから座標を生成し、xyzファイルを書き出す
#  書き出したファイルを読みコンソールへの表示もする
#引数(compound_obj_3d,compound_obj_2d) 1:3d情報をもつcompoundオブジェクト, 2: 2d情報のcompoundオブジェクト 
#戻り値(int) 1:最後まで実行できたら1を返す
def make_xyzfile(cmp_3d,cmp_2d):
    atm=cmp_3d.atoms
    with open(str(cmp_2d.iupac_name)+".xyz","w") as f:
        print(len(atm),end='\n',file=f)
        print("#",file=f)
        for x in atm:
            print("{} {:6.3} {:6.3} {:6.3}".format(x.element,float(x.x),float(x.y),float(x.z)),file=f)
    with open("hoge.xyz","r") as f:
        print("---write down my-written-xyzfile---")
        print(f.read())
    return 1
    

工夫として、生成するxyzファイルの名前をiupac_name.xyz としています。3d情報をもつcompoundオブジェクトは一部のプロパティを持たないためです。 (そのような仕様なのか、分からない。compound_3d.iupac_nameとしてもNoneをかえす。)

そのため2dと3dの両方のcompoundオブジェクトを渡し、使用しています

これで、xyzファイルができるので、可視化ソフトにかませると分子の描画を秒で行うとこができます。

プライバシーポリシー 免責事項

本ブログではすぐに利用できる(私含めて)ようにコードをまるまる載せているエントリーが多いです。使ってもらうことに問題はないのですが、それで不具合が生じても責任は負いませんよということ、免責事項とプライバシーポリシー等々については、以下のようになりますのでよろしくお願いします。

アクセス解析ツールについて

当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。 このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。 このトラフィックデータは匿名で収集されており、個人を特定するものではありません。 この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。

免責事項

当ブログからのリンクやバナーなどで移動したサイトで提供される情報、サービス等について一切の責任を負いません。

また当ブログのコンテンツ・情報について、できる限り正確な情報を提供するように努めておりますが、正確性や安全性を保証するものではありません。情報が古くなっていることもございます。

当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。

著作権について

当ブログで掲載している文章や画像などにつきましては、無断転載することを禁止します。

C構造体作ったリスト形式にデータを挿入する

以前にエントリーしたものの続き。

 構造体で実装したリスト構造にinsert()のような機能を追加した。

 

myprograming-tips.hatenablog.jp

 

やっていること自体に大きな変わりはなく、ポインタの操作を行っているだけ。

データの受け渡しはポインタで行う。構造体はそのままだとデータのコピーを渡す(はず)なので、このようにポインタで受け渡しするのがベターらしいです。

最初のうちは混乱するので、やはり紙に書いてまとめてみる方が分かりやすいと思います。

 

以下はコード例。追加したのはmyinsert()関数。他にもコメントを中心にちょっと以前のコードから少し修正

 


#include 
#include 
#include 

struct tfield{
    char hantei[8];
    int score;
    struct tfield *pointer;
};

//プロトタイプ宣言
struct tfield *genlist();
void display(struct tfield *p);
struct tfield *myinsert(struct tfield *p,int j,char *hnt,int scr);

int main(){
    struct tfield *p;
    //リストを生成する関数
    p=genlist();

    //リストの内容を表示
    printf("before insert \n");
    display(p);

    //4番目の箇所にデータを挿入
    p=myinsert(p,4,"E",0);

    //リストの表示
    printf("after insert \n");
    display(p);
}

//関数名 genlist()
//機能
//  コード内で記述のある配列を構造体のメンバ変数とし、
//  構造体型の変数型をもつリストを生成する関数
//引数():なし 
//戻り値(struct tfield*):struct tfield型のリスト先頭のアドレス
struct tfield *genlist(){
    int i=0;
    int array_score[]={90,80,70,60};
    char array_hantei[][2]={"A","B","C","D"};
    struct tfield *head,*mydata,*dummy;
    
    dummy=malloc(sizeof(struct tfield));
    head=dummy;
    while(1){
        mydata=malloc(sizeof(struct tfield));
        strcpy(mydata->hantei,array_hantei[i]);
        mydata->score=array_score[i];
        dummy->pointer=mydata;
        dummy=mydata;
        if(strcmp(mydata->hantei,"D")==0){break;}
        i=i+1;
    }
    mydata->pointer=NULL;
    return head;
}

// display(struct tfield *p)関数
//機能
//  リストの先頭のポインタを与えるとNULLになるまで表示する関数 
//引数(struct tfield*):リストの先頭のアドレス 
//戻り値:なし
void display(struct tfield *p){
    while(1){
        p=p->pointer;
        printf("%s %d %p \n",p->hantei,p->score,p->pointer);
        if(p->pointer==NULL){break;}
    }
}

//struct tfield *myinsert()関数
//機能
//  引数int jで指定したj番目の要素となるようにリストの間に構造体を挿入する
//  作成する構造体データも引数で与える
//引数(struct tfield*, int, char*,int):構造体のアドレス,挿入する箇所,struct tfieldの値,struct tfieldの値
//戻り値(struct tfield*):appendした後の構造体の先頭のアドレス
struct tfield *myinsert(struct tfield *p,int j,char *hnt,int scr){
    int i=0;
    struct tfield *newdata,*tmp,*head;
    head=p;
    
    //挿入データの作成
    newdata=malloc(sizeof(struct tfield));
    strcpy(newdata->hantei,hnt);
    newdata->score=scr;

    while(1){
        p=p->pointer;
        if(i==j-1){
    //挿入する箇所のひとつ前のデータのアドレスがp, 挿入する箇所の次のデータを指すアドレスはp->pointerにある
    //tmpに挿入後のデータのアドレスを一時保管
            tmp=p->pointer;
    //挿入か所の次のデータをさせいていたところをnewdataの先頭にする
            p->pointer=newdata;
    //そして挿入データであるnewdataの次のデータを指すアドレスは、保管しておいたtmpである。これを代入する
            newdata->pointer=tmp;
        }
        if(p->pointer==NULL ){break;};
        i=i+1;
    }
    return head;
}

before insert A 90 0x55d8b0eff2e0 B 80 0x55d8b0eff300 C 70 0x55d8b0eff320 D 60 (nil) after insert A 90 0x55d8b0eff2e0 B 80 0x55d8b0eff300 C 70 0x55d8b0eff320 D 60 0x55d8b0eff750 E 0 (nil)


0~3要素のリストに4要素目に代入するようにしたので、末尾にデータを挿入する形になっている。 

2文字以上のスペースを1文字分のスペースに置換する

 

編集したいと思うファイルがスペース区切りということが多いです。

しかし、

・スペースの数がまちまちで、やたら多い。

・先頭にもスペース

ということがありました。

Linuxコマンドのsedやらを使って整形するのも手ですが、処理するプログラムで読み取りの際に整形する方が楽です。

というわけで、使用するのはre module のre.sub()関数を使用しました。

re --- 正規表現操作 — Python 3.9.2 ドキュメント

以下は文字列を受取処理して文字列を返すプログラムです


import re

#関数名convert_space reモジュールを使って文字列を置換
# 機能
#   2文字以上の連続するスペースを1文字のスペースに、
#   先頭のスペースを削除
# 引数(str,int) 1:文字列 2:n行目
# 戻り値 str, 引数で受け取った文字列の変換後
def convert_space(line,i):
    #re (正規表現操作) を用いて、二連続以上するスペース('\s+')を1スペースに変換(re.sub()関数を使う)
    line=re.sub(r'\s+',' ',line)
    #先頭のスペース('^\s')をなくす
    line=re.sub(r'^\s','',line)
    #line=line.lstrip()  #これでも可能
    return line
    

先頭のスペースの削除は、str.strip() or str.lstrip()でも可能です。

ファイル読み取りの際に利用することが多いと思います。

以前の記事と併せて、すぐに利用できるコードは以下になります。

myprograming-tips.hatenablog.jp

 


import re

#関数名convert_space reモジュールを使って文字列を置換
# 機能
#   2文字以上の連続するスペースを1文字のスペースに、
#   先頭のスペースを削除
# 引数(str,int) 1:文字列 2:n行目
# 戻り値 str, 引数で受け取った文字列の変換後
def convert_space(line,i):
    #re (正規表現操作) を用いて、二連続以上するスペース('\s+')を1スペースに変換(re.sub()関数を使う)
    line=re.sub(r'\s+',' ',line)
    #先頭のスペース('^\s')をなくす
    line=re.sub(r'^\s','',line)
    #line=line.lstrip()  #これでも可能
    return line

#readline()で各行を読み取るプログラム
#各行をconvert_space()で編集
def check_readline_behave():
    with open("text.txt","r") as f:
        i=0
        while(1):
            line=f.readline()
            #最終行は0を返す。最終行でbreak
            if(not line):break
            print(i,"行目 変換前",line,end="")
            line=convert_space(line,i)
            print(i,"行目 変換後",line)
            i=i+1
    
#テストデータ生成用のプログラム 
def make_test_data():
    with open("text.txt","w") as f:
        print("  1",file=f)
        print(" ",file=f)
        print("2 3   4 5  6",file=f)
        print("   22 333   4444     55555   ",file=f)

if __name__=="__main__":
    make_test_data()
    check_readline_behave()

実行結果
0 行目 変換前   1
0 行目 変換後 1 
1 行目 変換前
1 行目 変換後
2 行目 変換前 2 3   4 5  6
2 行目 変換後 2 3 4 5 6
3 行目 変換前    22 333   4444     55555
3 行目 変換後 22 333 4444 55555