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

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

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

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

Q&A

解決済

5回答

930閲覧

存在する組み合わせを数えたい

midsum0323

総合スコア40

Python 3.x

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

0グッド

1クリップ

投稿2018/04/11 04:40

編集2018/04/11 05:57

例えば下のような文字列がある場合
あいう
あうえ

うい
この4つの文字列のうち同じ文字の組み合わせをもつ文字列を数えたいです。
上の場合、たとえば「あ」と「う」があるのは「あいう」と「あうえ」に含むので2、
「い」は「あいう」「い」「うい」に含むので3と数えたいです。
結果は下の表になります。

パターン
(あ)2
(い)3
(う)3
(え)1
(あ,い)1
(あ,う)2
(あ,え)1
(い,う)2
(あ,い,う)1
(あ,う,え)1

列1は組み合わせで、列2はその組み合わせを含む文字列の数です。
文字の長さがまちまち(最長十文字以上)で、かつ実際は数十万の文字列かつ漢字があるので上記の例の「いえ」など存在しない組み合わせまではできれば数えないようにしたいです。
現在のところ皆目見当がついていない状況なので何か教えていただけましと幸いです。

環境
Python3.6

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

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

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

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

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

KojiDoi

2018/04/11 04:48

「あ」は「あい」「あう」などにも含まれますが、こういうのは重複カウントするのですか?
midsum0323

2018/04/11 04:53

1文字の場合、2文字の場合...と全ての場合を数えたいです。
Zuishin

2018/04/11 04:54

「い」が 3 で「う」が 2 ですか? 仕様がよくわかりません。
can110

2018/04/11 04:55

列2の数値の意味は何ですか? また、列1の「あえ」が列2で「1」になっているのはどういうことを表していますか?
midsum0323

2018/04/11 04:59 編集

>「い」が 3 で「う」が 2 ですか? 仕様がよくわかりません。 「う」は3でした。失礼いたしました。 >列2の数値の意味は何ですか? 列1は組み合わせで、列2はその組み合わせを含む文字列の数です。たとえば「あう」の場合「あ」と「う」を含む文字列は「あいう」と「あうえ」の2つあるので2となります。説明不足ですみません。
KojiDoi

2018/04/11 05:02

よく読めば読むほどわからない質問文ですね。「文字列のなかに存在する文字」ここでいう「文字列」は列1に列挙してあるものですか、別のものですか? 「文字」は列1に列挙してあるものですか、別のものですか? 列2は質問と関係あるデータですか、関係ないデータですか?
KojiDoi

2018/04/11 05:06

「列1は組み合わせで、列2はその組み合わせを含む文字列の数です。」それなら「あ」は5じゃないの? ますますわからないよ。
midsum0323

2018/04/11 05:11

説明不足ですみません。質問文を書き換えました。
can110

2018/04/11 05:48

おおむね理解できたと思います。さらに「ああ」や「あうあ」のような同じ文字を複数含む文字列も考慮すべきですか?
midsum0323

2018/04/11 05:52

考慮していただけるとありがたいです
guest

回答5

0

ちょっと要求を正確に理解できているかどうかわからないのですが、つまりは1~n(n=文字数長)のn-gramがぜんぶ欲しいということですか?

python

1>>> s = "あいうえお" 2>>> lst = [] 3>>> for i in range(1, len(s)+1): 4... for j in range(len(s)-i+1): 5... lst.append(s[j:j+i]) 6... 7>>> lst 8['あ', 'い', 'う', 'え', 'お', 'あい', 'いう', 'うえ', 'えお', 'あいう', 'いうえ', 'うえお', 'あいうえ', 'いうえお', 'あいうえお']

追記

要求を理解できたので、素直に書いてみました。

python

1from itertools import chain, combinations 2 3lst = ["あいう", "あうえ", "い", "うい"] 4def make_combi(s): 5 s_set = set(s) 6 result = [] 7 for i in range(len(s_set)): 8 result.extend(sorted([tuple(sorted(x)) for x in combinations(s_set, i+1)])) 9 return result 10 11combi_result = [make_combi(s) for s in lst] 12chained_data = list(chain.from_iterable(combi_result)) 13index = sorted(list(set(chained_data))) 14result = [(key, chained_data.count(key)) for key in index] 15 16for key, val in result: 17 print(key, val) 18"""結果 19('あ',) 2 20('あ', 'い') 1 21('あ', 'い', 'う') 1 22('あ', 'う') 2 23('あ', 'う', 'え') 1 24('あ', 'え') 1 25('い',) 3 26('い', 'う') 2 27('う',) 3 28('う', 'え') 1 29('え',) 1 30"""

数十万件だとそのまま使うのは厳しいかもしれません。うまいやり方を考えるのは他の方に譲ります(あるいはご自身でどうぞ)。

投稿2018/04/11 04:54

編集2018/04/11 05:33
hayataka2049

総合スコア30933

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

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

midsum0323

2018/04/11 05:01

ご回答ありがとうございます。説明不足で恐縮ですが、Ngramではありません。 「あいうえお」の場合、「いえお」とか「あお」のパターンも数えたいです。
hayataka2049

2018/04/11 05:18

やりたいことは概ね理解できました(と思います)。異なり文字の組み合わせを列挙し(1~異なり文字数まで取り出す)、それでindexを作って出現する文書の数をカウントする、で合っていますか?
midsum0323

2018/04/11 05:56

ありがとうございます。試してみます。
hayataka2049

2018/04/11 06:05

1文中の重複はハナから無視しているので、chained_dataをdefaultdictでも使って数えれば速度はなんとかなる気がしてきました。これだとリストの走査が一回で済むので、count使うよりはだいぶマシ(なはず)です。 d = defaultdict(int) for x in chained_data: d[x] += 1
hayataka2049

2018/04/11 06:11

質問の追記・修正依頼で上がっている複数含む文字列を考慮するというのは、(あ,あ)という要素も対象にカウントするということですか? それだと私のコードは対応していないことをお知らせしておきます。対応させたい場合は、make_combiの一行目でsetにしているのをやめてください。
midsum0323

2018/04/11 06:13

コメントありがとうございます。助かります
guest

0

集計する際、Counterを使うと、便利だと思います。

python

1from collections import Counter 2from itertools import chain, combinations 3 4def realset(iterable): 5 "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)" 6 s = list(iterable) 7 return chain.from_iterable(combinations(s, r) for r in range(1:len(s)+1)) 8 9words = ["あいう", "あうえ", "い", "うい"] 10 11res = Counter() 12for count in [Counter(realset(x)) for x in words]: 13 res += count 14 15for key, value in res.items(): 16 print(key,value)

投稿2018/04/11 14:22

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

reでANDマッチングしてみました。

Python

1import re 2from itertools import combinations 3 4 5def make_charsets(words): 6 chars = sorted(set(''.join(words))) 7 charset_map = [ 8 list(combinations(chars, i)) 9 for i in range(1, len(chars) + 1) 10 ] 11 return [charset for line in charset_map for charset in line] 12 13 14def match_andpattern(charset): 15 andpattern = '^' + ''.join(['(?=.*' + c + ')' for c in charset]) 16 17 def _match(word): 18 return re.match(andpattern, word) != None 19 20 return _match 21 22 23def count_matching(charset, words): 24 return (charset, len(list(filter(match_andpattern(charset), words)))) 25 26 27words = ["あいう", "あうえ", "い", "うい"] 28charset_list = make_charsets(words) 29count_list = [count_matching(charset, words) for charset in charset_list] 30after = list(map(print, filter(lambda x: x[1] != 0, count_list)))

投稿2018/04/11 06:52

編集2018/04/11 06:57
arch_

総合スコア158

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

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

arch_

2018/04/11 07:38

ロジックがアレでした。
guest

0

ベストアンサー

素直に組むなら各文字列について1~N個の組み合わせを生成して既存分とチェックする感じですかね。
速度的にどうかですが。

なお「あああ」のように同じ文字を複数含む場合も考慮しています。

Python

1import itertools 2 3lines = ['あいう','あうえ','い','うい'] 4lines = [''.join( sorted(line)) for line in lines]# 文字の並びは関係ないので最初に昇順に 5 6combs = {} # キー:各文字列に出現しうる文字の組み合わせ 7 # 値 :出現した文字列の位置の集合。要素数=出現数 8 9for idx, line in enumerate( lines): 10 # N文字から 1...N文字でできる組み合わせ 11 for n in range(len(line)): 12 for i in itertools.combinations(line,n+1): 13 if i not in combs: 14 combs[i] = set() 15 combs[i].add(idx) 16 17for k,v in combs.items(): 18 print('{} ->{}個'.format(k,len(v)))

投稿2018/04/11 05:45

編集2018/04/11 06:01
can110

総合スコア38262

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

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

midsum0323

2018/04/11 06:13

ありがとうございます。試してみます
guest

0

たとえば、文字列Xが"a","b","c"の3文字をすべて含んでいるか否かは、次のコードで確認できます。

import re r = re.search("(?=.*a)(?=.*b)(?=.*c)", x)

上記abcにあたるものを「パターン」から持ってきて、rがNonoTypeでないならカウンタを+1する。そういうループを組めばいいのではないでしょうか。

投稿2018/04/11 05:26

KojiDoi

総合スコア13671

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問