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

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

ただいまの
回答率

90.52%

  • Python 3.x

    6387questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • pandas

    583questions

    Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

python3 dataframeの複数行の一括挿入について

解決済

回答 1

投稿 編集

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

wayaya

score 23

タイトルの件について、pythonのdataframeの複数行の一括挿入についての質問です。
下記のような事を行っているのですが、dataframeを挿入する関数(insert_data_frame)の実行時間が1~2秒程度で
1000~10000回程度呼び出しされるため、実行速度に悩んでいます。

そのため、start_indexとend_indexをリストとして格納して最終的に複数行を一括でoutput_DFに挿入できないかと考えたのですが、
方法が中々見つかりません。知っている方いれば教えてください。

・行っている事
1.数GB程度のinput_data.csvから特定条件の行をnp.whereでインデックスを抽出
2.抽出したインデックスが連続になっているか判定
3.連番でなくなった場合に、input_DFから対象となる範囲をoutput_DFに挿入
4.最終的なoutput_DFをlogとして出力

#!/usr/bin/python3
# -*- Coding: utf-8 -*-

import pandas as pd
import numpy as np


# 2つのDFを結合、inputDF2のtarget_row行をinputDF1のinsert_row行に挿入する。
def insert_data_frame(inputDF1, inputDF2, insert_row, target_row):
    insert_data = pd.DataFrame([eval("inputDF2.iloc[{}]".format(target_row))], index=[insert_row])
    output_df = insert_data.combine_first(inputDF1)

    return output_df


def main():
    input_DF = pd.read_csv("input_data.csv")
    output_DF = pd.DataFrame()
    series_flg = 0

    # 特定のインデックスを抽出
    target_index = np.where(input_DF.ix[:, "test"] == 1)

    for index in target_index[0]:
        # 抽出したインデックスが連番か判定
        if series_flg == 0:
            start_index = index
            end_index = index
            series_flg = 1

        elif (end_index + 1) == index:
            end_index = index

        # 連番でない場合は、出力用DFに挿入する
        else:
            if start_index == end_index:
                output_DF = insert_data_frame(output_DF, input_DF, len(output_DF), str(start_index))
            else:
                output_DF = insert_data_frame(output_DF, input_DF, len(output_DF), str(start_index) + ":" + str(end_index))

            # 抽出範囲を記載
            output_DF.ix[len(output_DF), "start_index"] = start_index
            output_DF.ix[len(output_DF), "end_index"] = end_index

            start_index = index
            end_index = index

    output_DF.to_csv("output.csv")

if __name__ == '__main__':
    main()
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+2

やりたいことがよくわからないのですが・・

順不同でいろいろ書きますと、

1.
現状 start_index と end_index が不一致の場合、範囲指定として  start_index:end_index となっておりますが start_index:end_index+1 とする必用があります。
また、start_index と end_index が一致の場合も同様にstart_index:end_index+1と記述できるので、そもそもこの箇所で処理を別ける必用はありません。

2.
insert_data_frame() 関数内で output_DF にデータを追加する処理ですが、一度 insert_data = pd.DataFrame(inputDF2.iloc[…]) として新しいデータフレームを生成した後に insert_data.combine_first(inputDF1) と追加処理を行っておりますが、この部分が無駄です。
新しいデータフレームを生成するのを止めて、indputDF2.iloc[…]の結果を直接inputDF2.iloc[…].combine_first(inputDF1) してください。

3.
2.で書いた、output_DF にデータを追加する際にワザワザ len(output_DF) のように output_DF の index値を求める処理をしているようですが、この部分も無駄ですので止めて、一番最後に output_DF.reset_index() などとして indexを振りなおすとだけで良いと思います。

4.
データを挿入する際に、start_index や end_index というカラムを作成して抽出範囲を追加する処理を行っておりますが、この処理も特に行う必用がないように思います。
3で書いたreset_index()の時に、drop=False(default値)を指定すると、元の index値を保持しますので、この値を使用するとよいのではないでしょうか。

5.
現状insert_data_frame() 関数の引数としてデータ範囲を文字列型 のデータを渡して、関数内で eval() により処理を行っておりますが、

dataframeを挿入する関数(insert_data_frame)の実行時間が1~2秒程度で1000~10000回程度呼び出しされるため、実行速度に悩んでいます。

というのであれば、数値→文字列→eval処理 の部分が一番のボトルネックになりそうです。
上で書いた箇所の修正を行うと、データを挿入する部分を特に関数化する必要がなくなりますのでループ内で直接 df.combine_first() を呼び出した方がよいのではないでしょうか。

ということで、上記をまとめるとこんな感じ

def main():
    input_DF = pd.read_csv("input_data.csv")
    output_DF = pd.DataFrame()
    series_flg = 0

    # 特定のインデックスを抽出
    target_index = input_DF[input_DF.ix[:, "test"] == 1].index

    for index in target_index:
        if series_flg == 0:
            start_index = index
            end_index = index
            series_flg = 1
        elif (end_index + 1) == index:
            end_index = index
        else:
            output_DF = input_DF.iloc[start_index:end_index+1,:].combine_first(output_DF)
            start_index = index
            end_index = index
    output_DF = output_DF.reset_index().rename(columns={'index':'org_index'})
    output_DF.to_csv("output.csv")

とココまで書きましたが、そもそも

def main():
    input_DF = pd.read_csv("input_data.csv")
    output_DF = input_DF[input_DF['test'] == 1].copy()
    output_DF.to_csv("output.csv")

では何で駄目なのでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/07 21:13

    回答ありがとうございます。
    30万行のcsvでテストしたところ元のコードが12時間36分だったのに比べ、6時間30分に減少していました。
    最後のコードがダメな理由は条件に合うログを取得したいというよりも、条件範囲の開始(start_index)と終了(end_index)インデックスを求めて場合によって取得するログの範囲を変えようと思っていたからです。

    キャンセル

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

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

関連した質問

同じタグがついた質問を見る

  • Python 3.x

    6387questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • pandas

    583questions

    Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。