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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

3回答

724閲覧

【Python】グループの重複をカウントする

退会済みユーザー

退会済みユーザー

総合スコア0

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

1グッド

0クリップ

投稿2023/02/09 13:26

編集2023/02/09 14:53

実現したいこと

  • 以下の左図のように、グループ(group)に属する人物(person)のリストがあります。
  • 各グループ内の人物が重複する人数をカウントし、右図のようにまとめたいと思っています。

  - AとBは「2」がどちらにも含まれているので、重複している人物が1人 → freq = 1
- AとDは「2」「3」がどちらにも含まれているので、重複している人物が2人 → freq = 2

  • グループは約3,000、人物はのべ5万人あるデータを処理します。

イメージ説明

試してみたこと

  • グループの組み合わせを作成。
  • グループの組み合わせを参照しながら、該当するグループの重複を数える方法がないかと悩んでします。

python

1import pandas as pd 2import itertools 3 4data = pd.read_table("C:/Users/lisaco/data_0131.txt", encoding="utf-8") 5dt = data[['group', 'person']] 6 7u = dt['group'].unique() 8comb = itertools.combinations(u,2) 9 10name = ["group1", "group2"] 11dtx = pd.DataFrame(comb, columns=name) 12 13~~def func(x): 14 df.groupby('comb')['b'].agg(lambda x: list(x)):~~ 15 16~~df['freq'] = df.apply(func, axis=1)~~

よろしくお願いします。

melian😄を押しています

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

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

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

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

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

y_waiwai

2023/02/09 13:36

どういう集計をする必要があるのか詳しく説明しよう
bsdfan

2023/02/09 23:29

解決済みなので、コメントで。 もし、速度を求めるなら、group × person の 0, 1 テーブルを作って、行列積をとるのが簡単ですよ。
退会済みユーザー

退会済みユーザー

2023/02/09 23:48

「丸投げ」と言われ、これが噂に聞いているアレかと思いました。たぶんもう投稿しません。 以前は、この素人が作ったロジックを提示したこともありましたが、どうにもならない意味不明さだったのでしょう、回答ゼロでした。 シンプルにやりたいことだけ書いたほうが良いのかもしれないとか、詰まっているポイントが言葉にならないから質問しないほうがいいのかもしれないとか、悩みながら質問しました。 私のような素人は、調べるのに相応しい単語、説明するのに適した単語さえ持っていないことが多いです。 教えていただいて、調べるキッカケを頂き、pythonの考え方に気づけたので、一気に理解が進みました。 melianさんには心から感謝致します。
退会済みユーザー

退会済みユーザー

2023/02/10 00:40

bsdfanさん、ありがとうございます。 行列積、調べてみます。
melian

2023/02/11 16:25

余談になりますが、Numpy による行列積(numpy.matmul)は int 型のデータ(numpy.ndarray)で実行すると BLAS を利用しませんのでご注意下さい。以下に挙げる回答では numpy.dot を取り上げていますが、numpy.matmul でも同様です。 https://stackoverflow.com/a/19839985 実際に、手元の環境でグループ数 3,000, 人物 50,000 で実行すると 10 分以上経過しても終わらず、dtype を numpy.float32 に変更してから実行すると1.2秒で終了します。ただ、行列積を実行するためのテーブルの作成に多少の時間が掛かりますので、回答した方法の 2 倍程度の高速化になっています。
bsdfan

2023/02/12 01:55

@melianさん ご指摘の通り、巨大な整数の行列での積は遅くなるんでした。今回のケースとかだとscipy.sparseを使うのが良いのかもしれない気がしてきました。 どれぐらい疎かによりますが、2倍よりは速くなるのでは。
退会済みユーザー

退会済みユーザー

2023/02/12 12:10

melianさん、bsdfanさん、ありがとうございます。 お二人の会話が高度で理解が追いつきませんが、研究させていただきます! 勉強になりました。ありがとうございます。
guest

回答3

0

group × person のテーブルを作って、重複をカウントする方法です。
コメントで int の行列積は遅くなるという指摘があったので、scipy.sparse を使ってみました。

python

1import pandas as pd 2import numpy as np 3from scipy.sparse import csr_matrix 4 5# df = pd.read_csv(...) 6 7group_uniq, group_idx = np.unique(df['group'], return_inverse=True) 8person_uniq, person_idx = np.unique(df['person'], return_inverse=True) 9 10group_person = csr_matrix( 11 (np.ones(len(group_idx), dtype=int), (group_idx, person_idx)), 12 shape=(len(group_uniq), len(person_uniq))) 13freq = (group_person @ group_person.T).toarray() 14 15# sparseを使わない場合 16# group_person = np.zeros((len(group_uniq), len(person_uniq))) 17# group_person[group_idx, person_idx] = 1 18# freq = (group_person @ group_person.T).astype(int) 19 20idx1, idx2 = np.triu_indices(len(group_uniq), 1) 21dfx = pd.DataFrame({ 22 'group1': group_uniq[idx1], 23 'group2': group_uniq[idx2], 24 'freq': freq[idx1, idx2] 25})

google colab上で、group数 3,000, person数 50,000, df行数 約1,000,000 のデータを作ってみて試したところ、以下の時間でした。
sparseを使うのが有効そうです。
sparse使用 : 1.98秒
sparseなし : 25.5秒
ベストアンサー : 90.0秒
sparseなし(int) : 数分まっても終わらず

投稿2023/02/12 01:54

編集2023/02/12 05:41
bsdfan

総合スコア4560

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

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

退会済みユーザー

退会済みユーザー

2023/02/12 12:15

bsdfanさん、ありがとうございます。 結構スパースなデータではないかと想像していますので、このようにご検討いただいて、大変感謝致します。 まだ速度まで考慮できるほど理解が追いつきませんが、きちんと理解できるようよくよく拝見します!
guest

0

ベストアンサー

※ 重複のない組み合わせ(freq == 0)を merge で補完。

python

1import pandas as pd 2from itertools import combinations 3 4df = pd.DataFrame({ 5 'group': [*['A']*4, *['B']*3, *['C']*5, *['D']*4], 6 'person': [1, 2, 3, 4, 2, 5, 6, 4, 7, 8, 9, 10, 2, 3, 7, 12], 7}) 8 9dfx = df.groupby('person')['group']\ 10 .apply(lambda g: [*combinations(g, 2)])\ 11 .explode().value_counts().sort_index().to_frame('freq') 12comb = pd.DataFrame(index=[*combinations(df['group'].unique(), 2)]) 13dfx = pd.merge(comb, dfx, left_index=True, right_index=True, how='left')\ 14 .fillna(0, downcast='infer') 15dfx = pd.DataFrame(dfx['freq'], index=pd.MultiIndex.from_tuples(dfx.index))\ 16 .rename_axis(['group1', 'group2']).reset_index() 17 18print(dfx)
group1group2freq
AB1
AC1
AD2
BC0
BD1
CD1

投稿2023/02/09 14:20

編集2023/02/09 18:26
melian

総合スコア19714

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

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

退会済みユーザー

退会済みユーザー

2023/02/09 23:31

melianさん、ありがとうございました。 mergeですね! from itertools import combinations ここから違う。 df.groupby('person')['group'].apply(lambda g: [*combinations(g, 2)])\ .explode().value_counts().sort_index().to_frame('freq') groupby ~~ apply(lambda~~ はこう使うんですね。「.」で繋いでいく・・勉強になります。 melianさんが教えてくださった内容、分解してモノにします! 「丸投げ」と言われ、これが噂に聞いているアレかと思いました。たぶんもう投稿しません。 以前は、この素人が作ったロジックを提示したこともありましたが、どうにもならない意味不明さだったのでしょう、回答ゼロでした。 シンプルにやりたいことだけ書いたほうが良いのかもしれないとか、詰まっているポイントが言葉にならないから質問しないほうがいいのかもしれないとか、悩みながら質問しました。 私のような素人は、調べるのに相応しい単語、説明するのに適した単語さえ持っていないことが多いです。 教えていただいて、調べるキッカケを頂き、pythonの考え方に気づけたので、一気に理解が進みました。 ありがとうございました。心から感謝致します。
guest

0

どのようなスクリプト~

  1. 人物毎に、属するグループのリストを求めます。
  2. 人物毎に、属するグループのリストから、それらの2つの組み合わせの組を列挙します。
  3. 2で求めた組毎の個数を求めます。

以上を求めるようなスクリプトを書けばよいと思います。

投稿2023/02/09 14:07

can110

総合スコア38262

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問