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

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

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

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

pandas

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

Q&A

解決済

1回答

238閲覧

グループ毎の特定の列の要素数を条件付きで数えたい & グループ毎の特定の列の要素を文字列連結して新規列に代入したい

jinyo

総合スコア59

Python

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

pandas

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

0グッド

0クリップ

投稿2019/03/31 11:49

はじめに

お世話になります。
Pandasで加工した下記のデータフレーム(以下DF)につきまして、求める要件2点があります。
実現方法についてお知恵をお貸しいただきたいです。

|品番|品名|部品|単価|生産数|
|:--|:--:|--:|--:|
|111|製品AAA|A|0.5|1,000|
|111|製品AAA|B2|0.5|1,000|
|111|製品AAA|C|0.5|1,000|
|111|製品AAA|ふくろ|0.5|1,000|
|111|製品AAA|箱|0.5|1,000|
|222|製品BBB|C|0.5|2,000|
|222|製品BBB|箱|0.5|2,000|
|333|製品CCC|A1|0.5|3,000|
|333|製品CCC|A2|0.5|3,000|
|333|製品CCC|箱|0.5|3,000|

やりたいこと

質問のタイトルが適切でないような気がして申し訳ないですが、やりたいことは下記の二つです。

要件1

上記のDFに新規列[部品項目数]という列を作り、商品毎の部品列の要素数のcountを出したいです。
これだけなら自分で解決できるのですが、下記の条件があります。

条件:部品列の"ふくろ"、"箱"という値に関してはカウントしない

商品毎に"ふくろ"だけがある商品、"箱"だけがある商品、両方ある、両方ない、パターンがあります。

要件2

商品毎の部品列の要素を文字列連結したいです。つまり商品Aであれば部品は["A","B2","C","ふくろ","箱"]がありますので、要件Aとおなじく"ふくろ","箱"は無視したそれ以外の値を、「"A,B2,C"」といったような形でひとつの文字列にし、そしてそれを新規列["部品一覧"]列に挿入したいです。

求める結果

つまり、下記のようなDFを取得したい、というのが目的になります。

|品番|品名|部品|単価|生産数|部品項目数|部品一覧
|:--|:--:|--:|--:|--:|
|111|製品AAA|A|0.5|1,000|3|A,B2,C
|222|製品BBB|C|0.5|2,000|1|C
|333|製品CCC|A1|0.5|3,000|2|A1,A2

解決のために実施したこと

品番の数だけ、行数を数えましたが、"ふくろ"、"箱"を無視する方法がわかりませんでした。

python

1 2pivot_df = df.pivot_table(values="総生産数" ,index=['品番'],columns="部品", 3 aggfunc="count" ,fill_value=0,dropna=False)

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

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

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

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

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

guest

回答1

0

ベストアンサー

要件1は、予め部品列の値が'ふくろ','箱'の行を削除したデータフレームを作成しておくと良いかと思います。
要件2に関しては、groupby.apply() または groupby.agg()などを使い Seriesをtupleやlistに変換することで実現できます。

Python

1import pandas as pd 2import io 3data = """ 4品番,品名,部品,単価,生産数 5111,製品AAA,A,0.5,1000 6111,製品AAA,B2,0.5,1000 7111,製品AAA,C,0.5,1000 8111,製品AAA,ふくろ,0.5,1000 9111,製品AAA,箱,0.5,1000 10222,製品BBB,C,0.5,2000 11222,製品BBB,箱,0.5,2000 12333,製品CCC,A1,0.5,3000 13333,製品CCC,A2,0.5,3000 14333,製品CCC,箱,0.5,3000 15""" 16 17df = pd.read_csv(io.StringIO(data)) 18tmp = df[~df['部品'].isin(['箱', 'ふくろ'])] 19res = tmp.groupby(['品番','品名']).agg({'単価':'first','生産数':'first','部品':['count',lambda d:tuple(d)]}) 20res.columns = ['単価','生産数','部品項目数','部品一覧'] 21print(res) 22# 単価 生産数 部品項目数 部品一覧 23#品番 品名 24#111 製品AAA 0.5 1000 3 (A, B2, C) 25#222 製品BBB 0.5 2000 1 (C,) 26#333 製品CCC 0.5 3000 2 (A1, A2)

【追記】

上記のコードを 先に groupby() してから agg() の中で "箱"と"ふくろ"を処理しないように修正してみます。

Python

1def conv_parts_to_tuple(d): 2 return tuple(d[~d.isin(['箱', 'ふくろ'])]) 3 4def num_of_parts(d): 5 return d[~d.isin(['箱', 'ふくろ'])].count() 6 7res = df.groupby(['品番','品名']).agg( 8 { 9 '単価':'first', 10 '生産数':'first', 11 '部品': [ num_of_parts, 12 conv_parts_to_tuple,] 13 }) 14res.columns = ['単価','生産数','部品項目数','部品一覧']

やっていることはほとんどど同じなのですが、 agg()内で2つのlambda(無名関数)を指定することが困難そうだったので、関数として切り出して処理しております。

"箱"と"ふくろ"の処理部が2つの関数に含まれて若干冗長な感じはあるのですが、この程度であればまあ個人的には問題ないかと思います。

ただ複数個の関数を切り出す必用があるのであれあ、apply()を使って1つの関数で処理したほうがシンプルになる気もしますね・・。

やってみます。

Python

1def fnc(row): 2 tmp = row.loc[~row['部品'].isin(['箱', 'ふくろ']), '部品'] 3 return pd.Series( 4 { 5 '単価': row['単価'].iat[0], 6 '生産数': row['生産数'].iat[0], 7 '部品項目数': tmp.count(), 8 '部品一覧': tuple(tmp) 9 }) 10 11res = df.groupby(['品番','品名']).apply(fnc) 12print(res)

apply()の場合は Series型のデータを返すことで列を構成します。

この方法であれば

  • "箱"と"ふくろ"の処理部を共通化できる
  • Column名を直に指定できるため変更の必要がない

のですこしシンプルになります。まあ、好みの問題ではありますが。。

投稿2019/03/31 14:22

編集2019/04/01 00:37
magichan

総合スコア15898

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

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

jinyo

2019/03/31 15:45

magichan様 お世話になります。素晴らしいです。最高です。 groupby.apply()やgroupby.agg()は知りませんでしたので早速明日トライしてみます! そして大変申し訳ないのですが、お伝え忘れたこととして、 「予め部品列の値が'ふくろ','箱'の行を削除したデータフレームを作成する」という、こちらは一度試したのですが採用しませんでした。採用しない理由といたしましては、部品列の要素として"箱"あるいは"ふくろ"しか持たない商品が存在しまして、商品まるごとフィルタされてしまったからです。説明足らずですみません。 ですので、aggのラムダ関数のところで、['箱', 'ふくろ']と一致しない場合にcountし、Seriesをtupleやlistにappendすれば良いのでしょうか? こちらで試してみて、うまくいかなければまた質問させていただく存じますのでもうしばらく質問をオープンにしておきたいと思います。何卒宜しくお願い致します。
magichan

2019/04/01 00:11 編集

「部品列の要素として"箱"あるいは"ふくろ"しか持たない商品が存在」 なるほど。その場合は '部品項目数'は "0" 、'部品一覧'は "()" で良いのでしょうかね。 とりあえず、その場合は 不要な行を削除してから``groupby()`` するのではなく、``groupby()`` してからその中で 不要な行を処理しないようにすることで対応できるかと思います。
jinyo

2019/04/02 15:44

お世話になります。確認遅くなりすみません。 素晴らしいです。どちらのコードも求める通りの結果を得ることができました。そしてapply関数の方がわかりやすかったのでそちらを使用することにいたしました。applyでシリーズを返す関数を呼ぶなんてことができるんですね。 わたしはpandasまだまだ勉強中で、抽出、並び替え、同じ値の一括代入、といった簡単なことしかできませんでしたので、これを使って色々出来そうなことが広がりそうでとても興奮しています・・! 大変助かりました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問