Python XYデータの傾きを対数log補完し、指定Yに交差するXを抽出する

本プログラムの概要は下図です。X,Yデータにおいて、Yデータを昇順(小→大)に順番に指定したY値を越えるかを調べます。もし、指定したYを越えたらその前後の(X, Y)値を取得して対数log補完により傾きを計算します。そして、その傾きを利用して指定したYに交差するXを抽出してファイルへ書き出します。

f:id:HK29:20190701190232p:plain

 ▼本プログラム実行後は、下図のようなextracted_data.txtファイルを出力します。図例では、Y=2.5e-10と交差するのはX=1.0283718977664713 ということです。

f:id:HK29:20190701193113p:plain

 ▼本プログラム

汎用性を持たせるために、基本パラメータは一か所へ、変数名に代入する仕様にしています。if __name__ == '__main__': 以下に、読み出すcsvファイル名、X,Yデータつまり列名の指定、データをソート(並び替え)またYデータを掛け算して前処理する値等を指定できます。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, sys, glob
import pandas as pd
import numpy as np
import math

class Extract_target:
    def __init__(self, my_path, my_startrow):
        self.df = pd.read_csv(my_path, skiprows=my_startrow, encoding="shift-jis")
        self.fname = my_path[:-4]
        print(self.df)
        
    def calc_X(self, my_columns, my_scale, my_sort, my_target):
        df2 = self.df[[my_columns[0], my_columns[1]]]
        df3 = df2.drop(0)
        df3_s = df3.sort_index(ascending=False) # 行名で並び替え。列名の場合axis=1追記
        
        add_column_name = my_columns[1] + " * " + str(my_scale)
        df3_s[add_column_name] = df3_s.apply(lambda x: f"{float(x[my_columns[1]]) * my_scale}", axis=1)
        print(df3_s)
        df3_s.to_csv(str(self.fname) + "_extract.csv")
        
        my_Ylist = df3_s[add_column_name].values.T
        my_Xlist = df3_s[my_columns[0]].values.T
        for i in range(len(my_Ylist)):
            if i==0:
                pass
            else:
                y2 = float(my_Ylist[i])
                x2 = float(my_Xlist[i])
                y1 = float(my_Ylist[i-1])
                x1 = float(my_Xlist[i-1])
                if ( y2 > my_target ) and ( y1 <= my_target ):
                    slope = (math.log10(y2) - math.log(y1))/(x2 - x1)
                    x = (math.log10(my_target) - math.log10(y1))/slope + x1
                    print("i -> " + str(i))
                    print("x1, y1 -> " + str(x1) + " " + str(y1))
                    print("x2, y2 -> " + str(x2) + " " + str(y2))
                    print("x -> " + str(x))
                    return x

        if y1 > my_target:
            return "NaN"

def write_target(my_outname, my_value):
    tmp_list = [my_outname, str(my_value), '
']
    with open("extracted_data.txt", "a") as f:
        f.writelines(tmp_list)

if __name__ == '__main__':
    ### input parameter
    my_path = "raw_data.csv"
    my_startrow = 0
    my_columns = ('C', 'D')   #columns name of x_data, y_data
    my_scale = 1/2   # <- y_data * my_scale
    my_sort = True
    my_target = 2.5e-10   # target Y value 
    my_outname = 'X@' + str(my_target) + "="  # name of extracted X
    
    ### call func
    DF = Extract_target(my_path, my_startrow)
    X = DF.calc_X(my_columns, my_scale, my_sort, my_target)
    write_target(my_outname, X)
    

以下は、もう少し詳しい補足です。

▼扱うデータの例(生データ)

下表のようなデータを「csvファイル」として準備します。(X, Y)は列名(A, B)、(C, D)などとしてプログラム内で指定して処理するため、何列あっても良いです。本記事のポイントは、BやDのような数値が桁で変化するような、対数(log)でデータ処理したい場合に関する。

A B C D
0 1.00E-15 11 1.00E-05
0.5 1.00E-14 10 6.00E-06
1 1.00E-13 9 4.00E-06
1.5 1.00E-12 8 2.00E-06
2 1.00E-10 7 1.00E-06
2.5 1.00E-09 6 6.00E-07
3 1.00E-08 5 4.00E-07
3.5 1.00E-07 4 2.00E-07
4 2.00E-07 3 2.00E-08
4.5 3.00E-07 2 2.00E-09
5 5.00E-07 1 2.00E-10
5.5 1.00E-06 0 2.00E-12
6 2.00E-06 -1 2.00E-13
6.5 3.00E-06 -2 2.00E-14
7 5.00E-06 -3 2.00E-15

▼実行処理の過程は、下図のようにprint関数で出力している。csvファイルを読み込み、指定した(X, Y)データ(例ではC,D)についてソートして、Yを0.5倍の前処理を行っています。そして、指定したY(例では2.5e-10)前後の(x1,y1)(x2,y2)と、(x, 指定したY)の値が出力された様子です。

f:id:HK29:20190701192951p:plain

以上

<広告>