Cucco’s Compute Hack

コンピュータ関係の記事を書いていきます。

シーケンシャルなデータに対して、固定のウィンドウ幅での演算を支援する関数

シーケンシャルなデータに対して、固定のウィンドウ幅での演算を支援する関数。mymap()。
指定されたウィンドウ幅でスライスして実際の演算関数に渡す。

シーケンシャルなデータは、1つまたは2つ。将来は可変長引数にしてもよいかもしれない。

コード

def mymap(function_name, window_size, list1, list2=None):
    len_ret =len(list1)
    ret = [None]*len_ret

    assert window_size > 0,"window_size > 0"
    assert type(window_size) is int , "window_size type must be int"

    if list2 is not None:
        assert len_ret == len(list2)

    if list2 is None:
        for i in range(window_size-1, len_ret):#長さ5の時、iの最大は4まで。
            # iはretの添え字
            tmp_list1=list1[i-window_size+1:i+1] #スライスの時、末尾:xのlist1[x]の値は含まれない。
            ret[i]=function_name(tmp_list1)
    else:
        for i in range(window_size-1, len_ret):
            tmp_list1=list1[i-window_size+1:i+1]
            tmp_list2=list2[i-window_size+1:i+1] 
            ret[i]=function_name(tmp_list1,tmp_list2)
    return ret
    
def myaverage(list1):
    return sum(list1)/len(list1)

def myadd(list1):
    return sum(list1)

def mymultadd(list1,list2):
    m=0
    for i in range(len(list1)):
        #print(list1[i] , "*",list2[i])
        m= m + list1[i] * list2[i]
    return m

def myEuclid(list1,list2):
    m=0
    for i in range(len(list1)):
        m = m + (list1[i] - list2[i])**2
    return m

if __name__ == '__main__':

    list1=[1,2,3,4,5]
    list2=[0,0,0,0,0]

    ret = mymap(myadd,1,list1)
    print(ret)
    ret = mymap(myadd,2,list1)
    print(ret)
    ret = mymap(myadd,3,list1)
    print(ret)
    ret = mymap(myadd,4,list1)
    print(ret)
    ret = mymap(myadd,5,list1)
    print(ret)

    ret = mymap(mymultadd,3,list1,list2)
    print(ret)

    ret = mymap(myEuclid,1,list1,list2)
    print(ret)

実行結果

[1, 2, 3, 4, 5]
[None, 3, 5, 7, 9]
[None, None, 6, 9, 12]
[None, None, None, 10, 14]
[None, None, None, None, 15]
[None, None, 0, 0, 0]
[1, 4, 9, 16, 25]

回帰直線を書いてみた

コード

import numpy as np
import matplotlib.pyplot as plt

x = list(range(0,31+1))
y = [28105,28070,27815,27680,27740,27470,27665,27635,27495,27315,27300,27285,27535,27635,27800,27845,27880,27830,27815,27775,27665,27865,27925,27745,28175,28195,28490,28660,28630,28735,29190,29640]

# フィッティング
a, b = np.polyfit(x, y, 1)
sv = np.std(y)

print(f"傾き={a}, 切片={b}, 標準偏差={sv}")

nx = np.array(x)

f=nx*a+b
# 1σ
fps=f+sv
fms=f-sv

plt.plot(x, y, '.', x,f,'k-',x,fps,'r-',x,fms,'b-')
plt.show()

結果

傾き=39.3154325513205, 切片=27347.01704545453, 標準偏差=528.5907478484063
f:id:Cucco:20210918171440p:plain

pythonでxlsファイルの数式をコピー(フィル)。

やりたいこと

pythonでxlsファイルの数式をコピー(フィル)。
相対参照や絶対参照ありで、参照関係をきちんと維持したままで。

コード

from openpyxl import load_workbook
from openpyxl.formula.translate import Translator
from openpyxl.utils.cell import coordinate_to_tuple

wb = load_workbook('translator.xlsx')
ws = wb["Sheet1"]

#絶対参照させる場所に値を設定
ws['G1'].value = 2

# 数式と、数式を埋め込む一番上のセルを指定。
origin_formula='=B2*C2*$G$1+1'
origin_cell="D2"
(origin_row, origin_col)= coordinate_to_tuple(origin_cell)

# originの列、2行目から一番したまで数式をフィル
# origin Cellに数式をあらかじめ埋めておく必要はない。
for row in ws.iter_rows(min_row=2, max_row=ws.max_row, min_col=origin_col, max_col=origin_col):
    ws[row[0].coordinate]= Translator(origin_formula,origin=origin_cell).translate_formula(row[0].coordinate)
    #print(ws[row[0].coordinate].value)

wb.save('translator_done.xlsx')

結果

f:id:Cucco:20210918123027p:plain
実行前
f:id:Cucco:20210918123136p:plain
実行後

月の第n曜日とか、月の最終曜日かどうかの判定

その月の第n曜日の計算と、月の最終曜日の判定関数。
指定の日付に対して、第N曜日の計算は単純に日付だけ。
逆に最終曜日の判定はちょっと面倒。

コード

import datetime

def nth_weekday(target_date : datetime.date):

    target_date_day=target_date.day
    if target_date_day <=7:
        return 1
    elif target_date_day <=14:
        return 2
    elif target_date_day <=21:
        return 3
    elif target_date_day <=28:
        return 4
    else:
        return 5

def is_last_weekday(target_date : datetime.date):
    this_month=target_date.month
    tmp_date = target_date + datetime.timedelta(days=7)
    tmp_month = tmp_date.month

    if this_month != tmp_month:
        return True
    else:
        return False

target_date=datetime.date(2021,1,1)
datetime.timedelta(days=1)

for i in range(366):
    print(target_date,nth_weekday(target_date),is_last_weekday(target_date))
    target_date= target_date+ datetime.timedelta(days=1)

記述のカテゴリ分け

もとの詳細な記述を分析して、下位のカテゴリに分類し、下位のカテゴリをさらに上位カテゴリに再分類する。
2階層のブレイクダウンの逆。

ルールは辞書で作成。keyにあたる部分がカテゴリ名、valueにはlistを設定する。
そのリストにある記述が、もとの詳細な記述の中にあれば、そのカテゴリであると判断する。

下位カテゴリが複数のカテゴリに該当する場合、もとの詳細な記述の最初に書いてあるカテゴリとする。

結果の見方は、「もとの詳細な記述 下位カテゴリ 上位カテゴリ」

結果

AA123 A1 A
AA123B22 A1 A
B22AA123 B2 B
asdf None None
asC12df C1 None

ソース

def classify_category(source_descripton,rule_dic):
    category_found = False
    found_key= ""
    find_pos=100 # 十分な長さ

    for category_key in rule_dic.keys():
        for category_value in rule_dic[category_key]:
            if category_value in source_descripton:
                category_found = True
                tmp_pos=source_descripton.find(category_value)
                if tmp_pos < find_pos:
                    found_key = category_key
                    find_pos=tmp_pos

    if category_found is False:
        found_key="None"

    return found_key

if __name__ == '__main__':
    top_category={}
    second_category={}

    top_category["A"]=["A1","A2"]
    top_category["B"]=["B1","B2"]

    second_category["A1"]=["A11","A12"]
    second_category["A2"]=["A21","A22"]
    second_category["B1"]=["B11","B12"]
    second_category["B2"]=["B21","B22"]
    second_category["C1"]=["C11","C12"]

    source_descripton="AA123"
    second_category_description = classify_category(source_descripton,second_category)
    top_category_description = classify_category(second_category_description,top_category)
    print(source_descripton,second_category_description,top_category_description)

    source_descripton="AA123B22"
    second_category_description = classify_category(source_descripton,second_category)
    top_category_description = classify_category(second_category_description,top_category)
    print(source_descripton,second_category_description,top_category_description)
    
    source_descripton="B22AA123"
    second_category_description = classify_category(source_descripton,second_category)
    top_category_description = classify_category(second_category_description,top_category)
    print(source_descripton,second_category_description,top_category_description)

    source_descripton="asdf"
    second_category_description = classify_category(source_descripton,second_category)
    top_category_description = classify_category(second_category_description,top_category)
    print(source_descripton,second_category_description,top_category_description)

    source_descripton="asC12df"
    second_category_description = classify_category(source_descripton,second_category)
    top_category_description = classify_category(second_category_description,top_category)
    print(source_descripton,second_category_description,top_category_description)

3次元の表

多次元(3次元)の配列が必要になったので、初期化と参照の方法を確認。
2次元表の中の1枠をcellとみなして、cellの中にも1次元の配列があるイメージ。

コード

import itertools

print("3次元の配列")
dim_row =2 # row
dim_col =3 # col
dim_cell =4 # cell

# ゼロ初期化
list_3d =[[[0 for z in range(dim_cell)] for y in range(dim_col)] for x in range(dim_row)]

print("\nlist_3d[0][1][2]=1")
list_3d[0][1][2]=1

print("\n単純にprint")
print(list_3d)

print("\n行を取り出してprint")
for i in list_3d:
    print(i)
    
print("\n行を、フラットな配列に変換")
for i in list_3d:
    print(list(itertools.chain.from_iterable(i)))

実行結果

3次元の配列

list_3d[0][1][2]=1

単純にprint
[[[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]

行を取り出してprint
[[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

行を、フラットな配列に変換
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

コンソールの同じ行にprint

コンソールで、表示を更新したいけど、行は増やしたくないときに使うもの。

消してもよい情報をprintするときは、printt.printt("hoge","piyo1",over_write=True) のように、"over_write=True” とする。

実行結果

('hoge', 'piyo0')
('hoge', 'piyo3')    
('hoge', 'piyo4')
('hoge', 'piyo7')    
('hoge', 'piyo9')   

コード

import time
class printt():
    # クラス変数
    last_over_write_status = False
    @classmethod
    def printt(cls, *args, over_write=False):

        if __class__.last_over_write_status is True:
            print("\r",end="")
        else:
            pass

        if over_write is False:
            if __class__.last_over_write_status is True:
                # 1行前の表示残りを上書きするため、後半に空白を追加
                print(args,"   ")
            else:
                print(args)
            __class__.last_over_write_status = False
        else:
            print(args,end="")
            __class__.last_over_write_status = True

if __name__ =="__main__":
    printt.printt("hoge","piyo0")
    time.sleep(1)
    printt.printt("hoge","piyo1",over_write=True)
    time.sleep(1)
    printt.printt("hoge","piyo2",over_write=True)
    time.sleep(1)
    printt.printt("hoge","piyo3")
    time.sleep(1)
    printt.printt("hoge","piyo4")
    time.sleep(1)
    printt.printt("hoge","piyo5",over_write=True)
    time.sleep(1) 
    printt.printt("hoge","piyo6",over_write=True)
    time.sleep(1)
    printt.printt("hoge","piyo7")
    time.sleep(1) 
    printt.printt("hoge","piyo8",over_write=True)
    time.sleep(1)
    printt.printt("hoge","piyo9")