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

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

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

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

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

Q&A

解決済

2回答

603閲覧

Pandas.DataFrameのデータ構造を変えたい(縦持ちから横持ちへ)

CookieM

総合スコア7

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

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

0グッド

0クリップ

投稿2022/08/31 10:51

前提

python 3.7.3
Anaconda Jupyter Notebook

実現したいこと

元データイメージ
イメージ説明
加工後データイメージ
「2019から2020にかけてID1はElemがAからBに変わった」と読めるような横持ちのデータにしたい。
イメージ説明

発生している問題・エラーメッセージ

エラーはありません

該当のソースコード

python

1import pandas as pd 2import numpy as np 3import itertools 4 5#元データ(上記元データイメージと同じもの) 6df = pd.DataFrame() 7df["Year"] = ["2019","2020","2021","2019","2020","2021","2019","2020","2021"] 8df["ID"] = [1,1,1,2,2,2,3,3,3] 9df["Elem"] = ["A","B","A","C","B","A","A","C","B"] 10 11#のちに結合する枠として、Yearの組み合わせをDataFrameの形で定義しておく 12year = df["Year"].drop_duplicates() 13year_list = year.sort_values().tolist() 14ptn = itertools.combinations(year_list, 2) #IDの2つの組み合わせを設定 15flame_df = pd.DataFrame([i for i in ptn], columns=["基準","比較"]) 16""" 17 基準 比較 180 2019 2020 191 2019 2021 202 2020 2021 21""" 22 23#一意のIDをリスト形式で保持 24ids = df["ID"].drop_duplicates() 25ids_list = ids.sort_values().tolist() 26 27#本処理 28output = pd.DataFrame() 29for i in ids_list: #idごとに処理を回す 30 tmp = flame_df.copy() 31 tmp["ID"] = i 32 #該当IDにおける基準年のElemをflame_dfにマージ 33 std_merged = pd.merge(tmp, 34 df[df["ID"]==i], 35 how='left', 36 left_on=['基準','ID'], 37 right_on=['Year','ID']).drop(columns='Year') 38 #該当IDにおける比較年のElemをflame_dfにマージ 39 both_merged = pd.merge(std_merged, 40 df[df["ID"]==i], 41 how='left', 42 left_on=['比較','ID'], 43 right_on=['Year','ID']).drop(columns='Year') 44 output = pd.concat([output, both_merged]) 45 46output.rename(columns={"Elem_x":"基準Elem", "Elem_y":"比較Elem"}, inplace=True) 47 48output 49 50""" 51 基準 比較 ID 基準Elem 比較Elem 520 2019 2020 1 A B 531 2019 2021 1 A A 542 2020 2021 1 B A 550 2019 2020 2 C B 561 2019 2021 2 C A 572 2020 2021 2 B A 580 2019 2020 3 A C 591 2019 2021 3 A B 602 2020 2021 3 C B 61"""

試したこと

縦持ちデータを横持にする際一般的にはpivot_tableを用いますが、
今回加工したい形に対応するaggfuncがないため、地道にやるしかないと思い、
上記のコードで実装してみました。

ただ実現はできたものの、IDの規模の規模が大きい場合、
IDごとに処理をループするこの書き方ではパフォーマンスに懸念があり・・・
より効率的な記述の作法がございましたらご意見をいただけますと幸いです。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答2

0

ベストアンサー

同じdf同士で、IDをキーにマージしてから、不要な行を削除してやるといいと思います。

python

1output = pd.merge( 2 df.rename(columns={'Year': '基準', 'Elem': '基準Elem'}), 3 df.rename(columns={'Year': '比較', 'Elem': '比較Elem'}), 4 on='ID' 5)[['基準', '比較', 'ID', '基準Elem', '比較Elem']] 6 7output = output[output['基準'] < output['比較']].reset_index(drop=True)

上では、カラム名を変更してからマージしていますが、マージしたあとカラム名を変更してもいいです。

投稿2022/08/31 15:37

bsdfan

総合スコア4560

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

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

CookieM

2022/09/01 00:05

ご回答ありがとうございます! とてもシンプルな記載で驚いています、パフォーマンス的にもとても優れいているためベストアンサーとさせていただきます。
guest

0

for文を使わない一例です。

Python

1import pandas as pd 2 3df = pd.DataFrame() 4df["Year"] = ["2019","2020","2021","2019","2020","2021","2019","2020","2021"] 5df["ID"] = [1,1,1,2,2,2,3,3,3] 6df["Elem"] = ["A","B","A","C","B","A","A","C","B"] 7 8df = df.sort_values(['ID', 'Year', 'Elem']) 9dfs = [] 10for i in range(1, df.groupby('ID').count().max().max()): 11 dfs.append(df.groupby('ID').apply(lambda x: x.shift(-i)).rename(columns={'Year':'比較', 'ID':'ID2', 'Elem':'比較Elem'})) 12temp = [] 13for i in range(len(dfs)): 14 temp.append(pd.concat([df, dfs[i]], axis=1)) 15df = (pd.concat(temp)).sort_values(['ID', 'Elem', 'Year']) 16df = df.dropna(subset=['比較']) 17df = df.drop('ID2', axis=1) 18df = df.rename(columns={'Year':'基準', 'Elem':'基準Elem'}) 19df = df[['基準', '比較', 'ID', '基準Elem', '比較Elem']] 20df = df.sort_values(['ID', '基準', '比較']) 21df = df.reset_index(drop=True) 22print(df) 23# 基準 比較 ID 基準Elem 比較Elem 24#0 2019 2020 1 A B 25#1 2019 2021 1 A A 26#2 2020 2021 1 B A 27#3 2019 2020 2 C B 28#4 2019 2021 2 C A 29#5 2020 2021 2 B A 30#6 2019 2020 3 A C 31#7 2019 2021 3 A B 32#8 2020 2021 3 C B

投稿2022/08/31 12:10

meg_

総合スコア10580

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

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

CookieM

2022/09/01 00:04

並び替え×シフトできれいに計算できるのは目から鱗でした。 爆速でのご回答ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問