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

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

ただいまの
回答率

90.34%

  • Python 3.x

    7398questions

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

python データフレーム 複数の列を条件として関数を適用させたい

解決済

回答 1

投稿

  • 評価
  • クリップ 2
  • VIEW 377

yuyu127

score 16

python のデータフレームで、複数の列を条件とした、新しい列(フラグ)を出力し、
それをもとに計算をしたいです。
下記2点質問があります。

質問1:複数列を条件としたapplyの適用
質問2:applyで作った新しい列をもとにした集計方法

詳細:
例として、曜日と時間の情報が入ったデータフレームに対し、各行毎に
「曜日-時間」という新しいフラグから、A、B、C列のそれぞれの値を計算したい。

#データフレーム例

df = pd.DataFrame({ 'A' : [0,0,10,15,0,0],
                    'B' : [10,12,3,0,0,16],
                    'C' : [0,0,0,10,11,12],
                    '曜日' : ['日','日','月','月','火','火'],
                    '時間' : [9,10,11,12,9,10]
                  })


A    B    C    時間    曜日
0    0    10    0    9    日
1    0    12    0    10    日
2    10    3    0    11    月
3    15    0    10    12    月
4    0    0    11    9    火
5    0    16    12    10    火

やりたいこと:A、B、C 毎に、「曜日」と「時間帯」の組み合わせの数字を平均する。
時間は(9,10,11)を朝、(12,13,14)を昼の時間帯とする。

質問1:複数列を条件としたapplyの適用
まずは、新たに「曜日-時間」の列を作成しましたが、
なるべくfor文を使わず、高速化、効率化のため「applyで適用」させたいです。
なにかよい方法はありますでしょうか。

下記でforで作ってみました。

df['曜日-時間'] = 0

for i in range(len(df)):
    if (df['曜日'][i]=='日') & (df['時間'][i] in (9,10,11)):
        df['曜日-時間'][i] = '日-朝'

    elif(df['曜日'][i]=='月') & (df['時間'][i] in (9,10,11)):
        df['曜日-時間'][i] = '月-朝'

    elif(df['曜日'][i]=='火') & (df['時間'][i] in (9,10,11)):
        df['曜日-時間'][i] = '火-朝'

    elif (df['曜日'][i]=='日') & (df['時間'][i] in (12,13,14)):
        df['曜日-時間'][i] = '日-昼'

    elif(df['曜日'][i]=='月') & (df['時間'][i] in (12,13,14)):
        df['曜日-時間'][i] = '月-昼'

    elif(df['曜日'][i]=='火') & (df['時間'][i] in (12,13,14)):
        df['曜日-時間'][i] = '火-昼'

    else:
        df['曜日-時間'] = 1


結果:
A    B    C    時間    曜日    曜日-時間
0    0    10    0    9    日    日-朝
1    0    12    0    10    日    日-朝
2    10    3    0    11    月    月-朝
3    15    0    10    12    月    月-昼
4    0    0    11    9    火    火-朝
5    0    16    12    10    火    火-朝

質問2:applyで作った新しい列をもとにした集計方法
A、B、C 毎に曜日と時間帯の組み合わせで平均する。
以下のようなデータフレームを最終的に作りたいのですが、何か効率的な
良い方法はありますでしょうか。

No    日-昼    日-朝    月-昼    月-朝    火-昼    火-朝
0    A    0    0    15    10    0    0.0
1    B    0    11    0    3    0    8.0
2    C    0    0    10    0    0    11.5

なにとぞよろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

曜日はそのまま採用、時間orに振り分ける関数を用意します。
この関数をdf.apply(にて行方向(axis=1)に適用します。

def to_wh(row):
    hour = '?'
    if row['時間'] in (9,10,11):
        hour = '朝'
    elif row['時間'] in (12,13,14):
        hour = '昼'
    return '{}-{}'.format(row['曜日'],hour)

import pandas as pd
df = pd.DataFrame({ 'A' : [0,0,10,15,0,0],
                    'B' : [10,12,3,0,0,16],
                    'C' : [0,0,0,10,11,12],
                    '曜日' : ['日','日','月','月','火','火'],
                    '時間' : [9,10,11,12,9,10]
                  })
df['曜日-時間'] = df.apply( to_wh, axis=1)
print (df)

# 集計
gr = df.groupby('曜日-時間')
print(gr.describe())
cols = ('A','B','C')
print(gr.mean().loc[:,cols])

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/01 13:37

    can110さん
    ありがとうございます。
    新列の作成はできました。
    そのあと、A、B、C毎に曜日-時間の平均を出したく、

    df.groupby('曜日-時間')['A'].mean()

    で出すことはできますが、A、B、Cの数が増えた場合に
    効率的にデータフレームを作ることはできますでしょうか。

    なにとぞよろしくお願いいたします。

    キャンセル

  • 2018/07/01 16:15

    推測ですが、groupby時点で各列の統計量が算出されているようなので
    単一列でも複数列でも効率は変わらないかと思います。
    なお、必要な複数列の結果を表示したい場合は
    回答に示したようにcolsを動的に作りこむことでコードは簡略化できます。

    キャンセル

  • 2018/07/01 22:46

    can110さん
    ありがとうございました。
    最後のデータフレームまでできました。
    大変助かりました。

    キャンセル

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

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

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

  • Python 3.x

    7398questions

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