はじめに
データベースのデータを整理しているときに、文字列型のカラムに入っている値をグループごとに結合したい場面がありましたので、メモとして残しておきます。
参考
- 
Pandas:グループ毎に括って最大の値を含む列を抜き出す 
 http://publicjournal.hatenablog.com/entry/2017/10/08/113544
- 
additional positional arguments 
 https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.apply.html
- 
時刻の計算と関数のDataFrameへの適用 
 https://qiita.com/Sasagawa0185/items/1185933dd0e560a26b07
環境
- Google Colabratory
- pandas==0.22.0
手順
次のようなデータを考えます。
import pandas as pd
df = pd.DataFrame({
    'id':['1','2','2','2','3','3'],
    'tag':['a','a','b','v','s','j'],
    'value':['70','23','64','23','12','9']
    })
データフレームは下記の形になります。
| id | tag | value | |
|---|---|---|---|
| 0 | 1 | a | 70 | 
| 1 | 2 | a | 23 | 
| 2 | 2 | b | 64 | 
| 3 | 2 | v | 23 | 
| 4 | 3 | s | 12 | 
| 5 | 3 | j | 9 | 
例えばidはユーザidで、tagは何らかの特徴を表す値、valueは支払い金額だとします。
ユーザごとグルーピングして支払い金額を集計しつつ、tagの値も残したいとします。
そこで、tagの値はアンダースコアで結合して残すことにします。
pandasでグループ化したのち、listをapplyすると、tagをリスト化してレコードに持つことができます。リストの中身をソートして、アンダースコアでjoinします。
result = (df.groupby('id')['tag']
          .apply(list)
          .apply(lambda x:sorted(x))
          .apply('_'.join)
         )
# 下記でもOK
# result = df.groupby('id')['tag'].apply(lambda x: '_'.join(sorted(list(x))))
# または
# result = df.groupby('id')['tag'].apply(lambda x: '%s' %
#                                        '_'.join(sorted(list(x)))).reset_index()
下記のように結合されます。
| id | tag | |
|---|---|---|
| 0 | 1 | a | 
| 1 | 2 | a_b_v | 
| 2 | 3 | j_s | 
元のデータフレームと同じサイズにする場合。
result = df.groupby('id')['tag'].transform(lambda x: '_'.join(sorted(x.tolist())))
おわりに
文字列にもgroupbyを使用して操作できると思ってない人も結構いるんじゃないでしょうか。
