質問: df.pivot_table(values="総生産数" ,index=['品番'],columns="部品", aggfunc="count" ,fill_value=0,dropna=False) ```
回答: 要件1は、予め部品列の値が'ふくろ','箱'の行を削除したデータフレームを作成しておくと良いかと思います。 要件2に関しては、`groupby.apply()` または `groupby.agg()`などを使い Seriesをtupleやlistに変換することで実現できます。 ``` import pandas as pd import io data = """ 品番,品名,部品,単価,生産数 111,製品AAA,A,0.5,1000 111,製品AAA,B2,0.5,1000 111,製品AAA,C,0.5,1000 111,製品AAA,ふくろ,0.5,1000 111,製品AAA,箱,0.5,1000 222,製品BBB,C,0.5,2000 222,製品BBB,箱,0.5,2000 333,製品CCC,A1,0.5,3000 333,製品CCC,A2,0.5,3000 333,製品CCC,箱,0.5,3000 """ df = pd.read_csv(io.StringIO(data)) tmp = df[~df['部品'].isin(['箱', 'ふくろ'])] res = tmp.groupby(['品番','品名']).agg({'単価':'first','生産数':'first','部品':['count',lambda d:tuple(d)]}) res.columns = ['単価','生産数','部品項目数','部品一覧'] print(res) # 単価 生産数 部品項目数 部品一覧 #品番 品名 #111 製品AAA 0.5 1000 3 (A, B2, C) #222 製品BBB 0.5 2000 1 (C,) #333 製品CCC 0.5 3000 2 (A1, A2) ``` **【追記】** 上記のコードを 先に `groupby()` してから `agg()` の中で "箱"と"ふくろ"を処理しないように修正してみます。 ``` def conv_parts_to_tuple(d): return tuple(d[~d.isin(['箱', 'ふくろ'])]) def num_of_parts(d): return d[~d.isin(['箱', 'ふくろ'])].count() res = df.groupby(['品番','品名']).agg( { '単価':'first', '生産数':'first', '部品': [ num_of_parts, conv_parts_to_tuple,] }) res.columns = ['単価','生産数','部品項目数','部品一覧'] ``` やっていることはほとんどど同じなのですが、 `agg()`内で2つのlambda(無名関数)を指定することが困難そうだったので、関数として切り出して処理しております。 "箱"と"ふくろ"の処理部が2つの関数に含まれて若干冗長な感じはあるのですが、この程度であればまあ個人的には問題ないかと思います。 ただ複数個の関数を切り出す必用があるのであれあ、`apply()`を使って1つの関数で処理したほうがシンプルになる気もしますね・・。 やってみます。 ``` def fnc(row): tmp = row.loc[~row['部品'].isin(['箱', 'ふくろ']), '部品'] return pd.Series( { '単価': row['単価'].iat[0], '生産数': row['生産数'].iat[0], '部品項目数': tmp.count(), '部品一覧': tuple(tmp) }) res = df.groupby(['品番','品名']).apply(fnc) print(res) ``` > > `apply()`の場合は Series型のデータを返すことで列を構成します。 > > > この方法であれば * "箱"と"ふくろ"の処理部を共通化できる * Column名を直に指定できるため変更の必要がない のですこしシンプルになります。まあ、好みの問題ではありますが。。