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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

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

pandas

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

Q&A

解決済

1回答

5480閲覧

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

wayaya

総合スコア30

Python 3.x

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

pandas

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

0グッド

0クリップ

投稿2017/10/03 14:08

編集2017/10/03 22:41

タイトルの件について、pythonのdataframeの複数行の一括挿入についての質問です。
下記のような事を行っているのですが、dataframeを挿入する関数(insert_data_frame)の実行時間が12秒程度で
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として出力

python

1#!/usr/bin/python3 2# -*- Coding: utf-8 -*- 3 4import pandas as pd 5import numpy as np 6 7 8# 2つのDFを結合、inputDF2のtarget_row行をinputDF1のinsert_row行に挿入する。 9def insert_data_frame(inputDF1, inputDF2, insert_row, target_row): 10 insert_data = pd.DataFrame([eval("inputDF2.iloc[{}]".format(target_row))], index=[insert_row]) 11 output_df = insert_data.combine_first(inputDF1) 12 13 return output_df 14 15 16def main(): 17 input_DF = pd.read_csv("input_data.csv") 18 output_DF = pd.DataFrame() 19 series_flg = 0 20 21 # 特定のインデックスを抽出 22 target_index = np.where(input_DF.ix[:, "test"] == 1) 23 24 for index in target_index[0]: 25 # 抽出したインデックスが連番か判定 26 if series_flg == 0: 27 start_index = index 28 end_index = index 29 series_flg = 1 30 31 elif (end_index + 1) == index: 32 end_index = index 33 34 # 連番でない場合は、出力用DFに挿入する 35 else: 36 if start_index == end_index: 37 output_DF = insert_data_frame(output_DF, input_DF, len(output_DF), str(start_index)) 38 else: 39 output_DF = insert_data_frame(output_DF, input_DF, len(output_DF), str(start_index) + ":" + str(end_index)) 40 41 # 抽出範囲を記載 42 output_DF.ix[len(output_DF), "start_index"] = start_index 43 output_DF.ix[len(output_DF), "end_index"] = end_index 44 45 start_index = index 46 end_index = index 47 48 output_DF.to_csv("output.csv") 49 50if __name__ == '__main__': 51 main() 52

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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

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

1.
現状 start_indexend_index が不一致の場合、範囲指定として start_index:end_index となっておりますが start_index:end_index+1 とする必用があります。
また、start_indexend_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_indexend_index というカラムを作成して抽出範囲を追加する処理を行っておりますが、この処理も特に行う必用がないように思います。
3で書いたreset_index()の時に、drop=False(default値)を指定すると、元の index値を保持しますので、この値を使用するとよいのではないでしょうか。

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

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

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

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

Python

1def main(): 2 input_DF = pd.read_csv("input_data.csv") 3 output_DF = pd.DataFrame() 4 series_flg = 0 5 6 # 特定のインデックスを抽出 7 target_index = input_DF[input_DF.ix[:, "test"] == 1].index 8 9 for index in target_index: 10 if series_flg == 0: 11 start_index = index 12 end_index = index 13 series_flg = 1 14 elif (end_index + 1) == index: 15 end_index = index 16 else: 17 output_DF = input_DF.iloc[start_index:end_index+1,:].combine_first(output_DF) 18 start_index = index 19 end_index = index 20 output_DF = output_DF.reset_index().rename(columns={'index':'org_index'}) 21 output_DF.to_csv("output.csv") 22

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

Python

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

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

投稿2017/10/04 01:10

magichan

総合スコア15898

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

wayaya

2017/10/07 12:13

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問