質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.55%

単回帰・重回帰解析に時間がかかりすぎてしまう

受付中

回答 0

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 83

Taka787

score 16

実現したいこと

説明変数に効果があるかどうかの解析を行いたくコードを実行していますが、説明変数リストが500個以上あり、実行終了に2時間以上かかっても終わりませんでした。具体的には、del_non_effective_explanatory_variable関数以降の処理が2時間以上経過しても終了しません。もっと高速に処理できる方法をご存じでしたら教えていただきたいです。
実行方法は以下のpythonファイルを実行します
※他にsql_select.pyとsql.pyというファイルが関係しているのですが、文字制限で添付不可

"""
「タイトルの特徴」のPVに対する寄与度を回帰分析によって求める。
本モジュールにはビジネスロジックを記述。
目的変数Y = pv, facebookからのpv等
説明変数X = 「特定キーワードを含む」場合1、その他0 etc
単回帰分析で効果のある特徴を絞り込み→相関係数が高い特徴を削除→重回帰分析で効果検証。
"""
from builtins import float

import psycopg2
from pandas import read_sql
from pandas.core.series import Series
from pandas.core.frame import DataFrame
from statsmodels.api import add_constant
from statsmodels.api import OLS
from sql import make_sql
import sql_select

#以下DB接続情報
DB_HOST = '***'
DB_NAME = '***'
DB_PORT = '***'
DB_USER = '***'
DB_PSWD = '***'

MAX_P = 0.1      # P値。この値より小さい場合を有意と認める上限値
MIN_BETA = 0.05  # 回帰係数。この値より絶対値が大きい場合を効果ありと認める
MAX_COR = 0.3    # 相関係数。この値より値が大きい場合を正相関ありとして片方の説明変数を除外する


def output_result(xs: list) -> None:
    """
    各説明変数ごとの該当数、回帰係数、P値、効果判定、増加率を標準出力。

    @param xs: 説明変数情報リスト
    """
    for x in xs:
        print("{}\t{}\t{}\t{}\t{}\t{}".format(x["NAME"], x["COUNT"], x["BETA"],
                                              x["P"], x["EFFECT"], x["RATE_OF_INCREASE"]))


def ols_simple(y_data: Series, x_data: Series) -> tuple:
    """
    単回帰分析を行い、該当件数と回帰係数とP値を求めてタプルにして返す。

    @param y_data: 目的変数データ
    @param x_data: 説明変数データ
    @return: (該当件数, 回帰係数(β), P値(P))
    """
    num_match = list(x_data).count(1)  # 該当数

    x_data = add_constant(x_data, prepend=False)  # 説明変数に定数項を加える
    results = OLS(y_data, x_data).fit()           # (Ordinary Least Squares: 最小二乗法)

    for beta_value, p_value in zip(results.params.to_dict().items(), results.pvalues.to_dict().items()):
        if beta_value[0] == p_value[0] and p_value[0] != "const":
            return num_match, beta_value[1], p_value[1]

    """
    単回帰分析の結果から説明変数の効果判定を行い、不要なものを削除した
    DataFrame(df)と説明変数リスト(xs)を返す。
    xs内のdictにはkeyとしてCOUNT, BETA, P, RATE_OF_INCREASE, EFFECTが追加される。

    @param df: DataFrame
    @param y_name: 目的変数データ名
    @param xs: 説明変数情報リスト
    @return: (修正されたdf, 修正されたxs)
    """

def del_non_effective_explanatory_variable(df: DataFrame, y_name: str, xs: list) -> tuple:
    df_tmp = df.copy()
    xs_tmp = list(xs)

    del_xs = []  # 効果がないので削除するxsの部分list
    for x in xs_tmp:
        count, beta, p = ols_simple(df[y_name], df_tmp[x["AS"]])
        x["COUNT"] = count
        x["BETA"] = beta
        x["P"] = p
        x["RATE_OF_INCREASE"] = 10 ** x["BETA"] - 1
        if x["P"] < MAX_P and x["BETA"] > MIN_BETA:
            x["EFFECT"] = " +"
        elif x["P"] < MAX_P and x["BETA"] < -MIN_BETA:
            x["EFFECT"] = " -"
        else:
            x["EFFECT"] = ""
            del_xs.append(x)

    output_result(xs_tmp)
    for dx in del_xs:
        print("{} は効果がありません".format(dx["NAME"]))
        del(df_tmp[dx["AS"]])
        xs_tmp.remove(dx)
    return df_tmp, xs_tmp


def del_high_corr_explanatory_variable(df: DataFrame, y_name: str, xs: list) -> tuple:
    """
    説明変数どうしの相関係数をチェックして、MAX_COR以上の場合は片方を削除した
    DataFrame(df)と説明変数リスト(xs)を返す。
    Pが小さいを優先。|β|が大きいを第二優先。

    @param df: DataFrame
    @param y_name: 目的変数データ名
    @param xs: 説明変数情報リスト
    @return: (修正されたdf, 修正されたxs)
    """
    df_tmp = df.copy()
    xs_tmp = list(xs)

    del(df_tmp["id"])
    del(df_tmp["title"])
    del(df_tmp[y_name])
    del_xs = []  # 相関が高いので削除するxsの部分list
    for i, x in enumerate(xs_tmp):
        for j, x2 in enumerate(xs_tmp):
            if i < j and df_tmp.corr()[x["AS"]][x2["AS"]] > MAX_COR and x not in del_xs and x2 not in del_xs:
                if x["P"] > x2["P"]:  # Pが小さい方残す
                    print("『{0}』と『{1}』の相関が高いので、『{0}』を削除します".format(x["NAME"], x2["NAME"]))
                    del_xs.append(x)
                elif x["P"] < x2["P"]:
                    print("『{0}』と『{1}』の相関が高いので、『{0}』を削除します".format(x2["NAME"], x["NAME"]))
                    del_xs.append(x2)
                else:
                    if abs(x["BETA"]) > abs(x2["BETA"]):  # Pが同一なら|β|が大きい方を残す
                        print("『{0}』と『{1}』の相関が高いので、『{0}』を削除します".format(x2["NAME"], x["NAME"]))
                        del_xs.append(x2)
                    else:
                        print("『{0}』と『{1}』の相関が高いので、『{0}』を削除します".format(x["NAME"], x2["NAME"]))
                        del_xs.append(x)

    df_tmp = df.copy()
    for dx in del_xs:
        del(df_tmp[dx["AS"]])
        xs_tmp.remove(dx)
    return df_tmp, xs_tmp


def ols_multiple(y_data: Series, x_data: DataFrame) -> list:
    """
    重回帰分析を行い、回帰係数とP値を求めて、list((変数名, 回帰係数, P値))を返す。

    @param y_data: 目的変数データ
    @param x_data: 説明変数データ
    @return: (変数名, 回帰係数(β), P値(P))
    """
    x_data = add_constant(x_data, prepend=False)  # 説明変数に定数項を加える
    results = OLS(y_data, x_data).fit()           # (Ordinary Least Squares: 最小二乗法)

    beta_p_values = []
    for beta_value, p_value in zip(results.params.to_dict().items(), results.pvalues.to_dict().items()):
        if beta_value[0] == p_value[0] and p_value[0] != "const":
            beta_p_values.append((p_value[0], beta_value[1], p_value[1]))
    return beta_p_values


def update_xs_with_ols_multiple(df: DataFrame, y_name: str, xs: list) -> list:
    """
    重回帰分析の結果からxs内dictのkey=(COUNT, BETA, P, RATE_OF_INCREASE, EFFECT)を更新して
    xsを返す。

    @param df: DataFrame
    @param y_name: 目的変数データ名
    @param xs: 説明変数情報リスト
    @return: 修正されたxs
    """
    xs_tmp = list(xs)
    x_data_name_list = []  # 説明変数データ識別名のリスト
    for x in xs_tmp:
        x_data_name_list.append(x["AS"])
    results = ols_multiple(df[y_name], df[x_data_name_list])
    # xs_tmpの更新
    for result in results:
        for x in xs_tmp:
            if result[0] == x["AS"]:
                x["BETA"] = result[1]
                x["P"] = result[2]
                x["RATE_OF_INCREASE"] = 10 ** x["BETA"] - 1
                if x["P"] < MAX_P and x["BETA"] > MIN_BETA:
                    x["EFFECT"] = " +"
                elif x["P"] < MAX_P and x["BETA"] < -MIN_BETA:
                    x["EFFECT"] = " -"
                else:
                    x["EFFECT"] = ""
    return xs_tmp

#
# main
#
ys = list(sql_select.ys)  # 目的変数のlist ex. {"SELECT": "log(pv_1w.pv + 1)", "AS": "pv"}
for y in ys:
    y_name = y["AS"]
    print("■■ 目的変数 = {} ■■".format(y_name))
    xs = list(sql_select.xs)  # 説明変数のlist
    sql = make_sql(y, xs)
#    print(sql)

    # DB → DataFrame
    with psycopg2.connect(host=DB_HOST, database=DB_NAME, port=DB_PORT, user=DB_USER, password=DB_PSWD) as conn:
        df = read_sql(sql, conn)
    df, xs = del_non_effective_explanatory_variable(df, y_name, xs)  # 単回帰分析でdf, xsから効果のない説明変数と対応データを削除
    print("\n-------------------------------------------")
    print("■ 相関係数のチェック")
    df, xs = del_high_corr_explanatory_variable(df, y_name, xs)      # 相関関数をチェックしてdf, xsを更新
    print("\n-------------------------------------------")
    xs = update_xs_with_ols_multiple(df, y_name, xs)                 # 重回帰分析で得られた結果でxsを更新
    print("■ 重回帰分析の結果")
    output_result(xs)
    print("\n\n=========================================")
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正の依頼

  • sazi

    2020/03/26 14:45

    どの処理にどの程度時間が掛かっているか位は追記されて方が良いかと思います。

    キャンセル

  • Taka787

    2020/03/26 14:52

    ご指摘ありがとうございます。
    情報追加しました

    キャンセル

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.55%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる