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

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

ただいまの
回答率

88.92%

Pandasデータフレームの結合

解決済

回答 1

投稿 編集

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

SatokoM

score 4

Python、Pandasを使ったデータフレーム結合に関する質問です。

下記のような色々な種類の果物の数量をDF_1と仮定しています。

Bascket Grape Apple Orange
0 50 50 0
1 0 20 80
2 0 70 30

各果物は下記DF_2のような属性を持っています。

Name Weight Price Suger_content
0 Grape     10 500 3
1 Apple     20 200 5
2 Orange 15 100 2

DF_1で各果物の値が0より大きい場合に果物の属性DF_2を横持ちで追加したいです。

Bascket Grape Apple Orange weight_1 price_1 Suger_content_1 weight_2 price_2 Suger_content_2
0 50 50 0 10 500 3 20 200 5
1 0 20 80 20 200 5 15 100 2
2 0 70 30 ... ... ... ... ... ...

該当のソースコード

for i, _ in df_1.iterrows():
    for i2, _ in df_2.iterrows():
        if df_2['Name'][i] in df_1.columns:
            df_1[i].append(df_2[i2])

試したこと

よろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • aokikenichi

    2020/07/09 17:58

    _1 が Grape
    _2 が Apple
    _3 が Orange
    と紐づいているわけではなく
    各果物の値が0以外の場合に左から詰めて_1、_2が付与されるということでよいでしょうか

    Bascket0のweight_2はAppleの20が入るのではないのでしょうか?

    出来なくはないと思いますがテーブル構造はシンプルにして集計で工夫した方がよいと思います。

    キャンセル

  • SatokoM

    2020/07/10 08:22

    >各果物の値が0以外の場合に左から詰めて_1、_2が付与されるということでよいでしょうか
    その通りです。二種類の果物の組み合わせだけで考えたいので左から詰めて_1、_2に格納したいです。

    >Bascket0のweight_2はAppleの20が入るのではないのでしょうか?
    ご指摘ありがとうございます。訂正いたしました。

    キャンセル

  • kirara0048

    2020/07/10 10:00 編集

    DF_1の各行につき、0になっている果物は常に1つという理解で良いですか?

    もしそうでない場合、
    ・3つの果物すべてが50の場合、結果テーブルは_2までで打ち切るのか、_3まですべて含めるのか
    ・3つの果物すべてが0の場合、結果テーブルにはどのような値が入るのか
    よろしくお願いします。

    キャンセル

回答 1

checkベストアンサー

0

「DF_1の各行につき、0以上の果物の種類の数は常に同じ」の場合の方法です。

import numpy as np
import pandas as pd

df_1 = pd.DataFrame({'Bascket': [0, 1, 2],
                     'Grape': [50, 0, 0],
                     'Apple': [50, 20, 70],
                     'Orange': [0, 80, 30]})
#    Bascket  Grape  Apple  Orange
# 0        0     50     50       0
# 1        1      0     20      80
# 2        2      0     70      30

df_2 = pd.DataFrame({'Name': ['Grape', 'Apple', 'Orange'],
                     'Weight': [10, 20, 15],
                     'Price': [500, 200, 100],
                     'Suger_content': [3, 5, 2]})
#      Name  Weight  Price  Suger_content
# 0   Grape      10    500              3
# 1   Apple      20    200              5
# 2  Orange      15    100              2

このとき、

array_left = df_1.to_numpy()
_, idx = array_left[:, 1:].nonzero()
array_right = df_2.iloc[:, 1:].to_numpy()[idx].reshape(len(array_left), -1)
pd.DataFrame(np.hstack((array_left, array_right)),
             columns=[*df_1.columns,
                      *[n + '_1' for n in df_2.columns[1:]],
                      *[n + '_2' for n in df_2.columns[1:]]])
#    Bascket  Grape  Apple  Orange  Weight_1  Price_1  Suger_content_1  Weight_2  Price_2  Suger_content_2
# 0        0     50     50       0        10      500                3        20      200                5
# 1        1      0     20      80        20      200                5        15      100                2
# 2        2      0     70      30        20      200                5        15      100                2

考え方

DF_1をndarrayに変換した後、まずnp.nonzero()を用いて、各列に対して「数量が0以上の果物が何であるか」という配列を作成します。
なお今回の例では存在しませんが、もしDF_1において果物の数量に負の値(0未満)が入っている場合は、0に変換してください。

In [11]: array_left = df_1.to_numpy()
       : array_left[array_left<0] = 0  # 数量が全て0以上の場合この行は不要
       : _, idx = array_left[:, 1:].nonzero()
       :
       : idx.reshape(-1, 2)
Out[11]:                      # (0, 1, 2) はそれぞれ (Grape, Apple, Orange) を表す
array([[0, 1],                # ←0列目は0=Grape, 1=Apple
       [1, 2],                # ←1列目は1=Apple, 2=Orange
       [1, 2]], dtype=int64)  # ←2列目は1=Apple, 2=Orange

次に、この配列をインデックスにして、ndarrayに変換したDF_2から各値を引っ張ってきます。
なお今回の例では問題ありませんが、DF_2の果物行の並び順が、DF_1の果物列の並び順(Grape→Apple→Orange)と異なる場合は、同じ順番にしてからndarrayに変換してください。

In [12]: array_2 = df_2.iloc[:, 1:].to_numpy()
       : array_2 = df_2.set_index('Name').loc[df_1.columns[1:]].to_numpy()  # 並び順が異なる場合
       : array_2
Out[12]:
array([[ 10, 500,   3],                # ←0=Grapeのデータ
       [ 20, 200,   5],                # ←1=Appleのデータ
       [ 15, 100,   2]], dtype=int64)  # ←2=Orangeのデータ


In [13]: array_right_pre = array_2[idx.reshape(-1, 2)]
       : array_right_pre
Out[13]:
array([[[ 10, 500,   3],        # 0列目  # 0=Grape のデータ
        [ 20, 200,   5]],                # 1=Apple のデータ

       [[ 20, 200,   5],        # 1列目  # 1=Apple のデータ
        [ 15, 100,   2]],                # 2=Orange のデータ

       [[ 20, 200,   5],        # 2列目  # 1=Apple のデータ
        [ 15, 100,   2]]], dtype=int64)  # 2=Orange のデータ


In [14]: array_right = array_right_pre.reshape(len(array_left), -1)
       : array_right
Out[14]:
array([[ 10, 500,   3,  20, 200,   5],
       [ 20, 200,   5,  15, 100,   2],
       [ 20, 200,   5,  15, 100,   2]], dtype=int64)

最後に結合してデータフレームに変換します。列名は手動で適切なものを設定してください。

In [15]: data = np.hstack((array_left, array_right))
       :
       : data
Out[15]:  # <----left---->  <-----------right---------->
array([[  0,  50,  50,   0,  10, 500,   3,  20, 200,   5],
       [  1,   0,  20,  80,  20, 200,   5,  15, 100,   2],
       [  2,   0,  70,  30,  20, 200,   5,  15, 100,   2]], dtype=int64)


In [16]: pd.DataFrame(data,
       :              columns=[*df_1.columns,
       :                       *[n + '_1' for n in df_2.columns[1:]],
       :                       *[n + '_2' for n in df_2.columns[1:]]])
Out[16]:
   Bascket  Grape  Apple  Orange  Weight_1  Price_1  Suger_content_1  Weight_2  Price_2  Suger_content_2
0        0     50     50       0        10      500                3        20      200                5
1        1      0     20      80        20      200                5        15      100                2
2        2      0     70      30        20      200                5        15      100                2

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/07/15 15:02

    ご回答いただきありがとうございます。
    解説も丁寧にしていただいたので理解することができ、今後応用もできそうです。
    ありがとうございました。

    キャンセル

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

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

関連した質問

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