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

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

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

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

Python

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

Q&A

解決済

1回答

4175閲覧

【Python3】2つのリストを比較して,要素の順番通り一致した箇所を置換したい

k_k_

総合スコア11

Python 3.x

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

Python

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

0グッド

2クリップ

投稿2019/05/24 07:54

編集2019/05/24 13:39

前提・実現したいこと

Python3にて,サイズの異なる2つのリストを比較して,置換元のリストに対して,置換対象のリストの要素の順番通り一致した箇所があれば,その要素を置換したいと考えています.
少し言葉にしづらいので,具体的な動作を以下のコードで示します.

ソースコード

Python3

1rule = { 2 "STUDENT" : [["生徒"], ["学生"]], 3 "STUDENTS" : [["STUDENT", "たち"], ["STUDENT", "ら"]], 4 "TEACHER" : [["先生"], ["教師"], ["教員"]], 5 "TEACHERS" : [["TEACHER", "たち"], ["TEACHER", "ら"]], 6 "FRUIT_EAT" : [["リンゴ", "を", "食べた"], ["ミカン", "を", "食べた"]] 7} 8a = ["生徒", "が", "リンゴ", "を", "食べた"] 9b = ["教師", "たち", "は", "ミカン", "を", "食べた"] 10c = ["教員", "と", "学生", "ら", "は", "ブドウ", "を", "食べた"] 11 12l = [a, b, c] 13 14for i, sent in enumerate(l): 15 s = "/".join(sent) 16 for key, val in rule.items(): 17 for v in val: 18 v = "/".join(v) 19 if v in s: 20 s = s.replace(v, key) 21 l[i] = s.split("/") 22 23print(l) 24 25 26# 結果 27# [['STUDENT', 'が', 'FRUIT_EAT'], 28# ['TEACHERS', 'は', 'FRUIT_EAT'], 29# ['TEACHER', 'と', 'STUDENTS', 'は', 'ブドウ', 'を', '食べた']]

辞書ruleのキーがカテゴリ名,値が置換対象のリストです.
このruleに基づいて,置換元のリスト(a, b, c)の要素に対して,順番通り一致したら,その箇所をカテゴリ名に置換するといった動作をしています.

上記のコードで一応やりたいことはできましたが,ネストが深くなってしまっているため,もう少し簡潔に記述したいと考えています.

よろしくお願いします.

試したこと

以下のように,リスト内包表記も試してみましたが,"="のような代入式が使えないため,リストの要素の置換が上手くできませんでした(使い方が悪いだけかもしれませんが...).

Python3

1for i, sent in enumerate(l): 2 s = "/".join(sent) 3 print([[s.replace("/".join(v), key) for v in val if "/".join(v) in s] for key, val in rule.items()]) 4 5 6# 結果 7# [['STUDENT/が/リンゴ/を/食べた'], [], [], [], ['生徒/が/FRUIT_EAT']] 8# [[], [], ['TEACHER/たち/は/ミカン/を/食べた'], [], ['教師/たち/は/FRUIT_EAT']] 9# [['教員/と/STUDENT/ら/は/ブドウ/を/食べた'], [], ['TEACHER/と/学生/ら/は/ブドウ/を/食べた'], [], []]

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

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

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

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

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

k_k_

2019/05/26 10:49

他に回答がありませんでしたので、 jun68ykt様の回答をベストアンサーとさせていただきます。
guest

回答1

0

ベストアンサー

こんにちは

正規表現を使う方法を考えました。

置き換え対象のリスト sent に、置き換えルール集 rule を適用した結果のリストを返す、以下のような関数を用意しておきます。

python3

1import re 2 3 4def replace_sentence(sent, rule): 5 sent = '/'.join(sent) 6 for key, val in rule.items(): 7 regexp = '|'.join(['/'.join(e) for e in val]) 8 sent = re.sub(regexp, key, sent) 9 10 return sent.split('/')

上記の replace_sentence(sent, rule) を使うことにより、l = [a, b, c] に対する処理は、以下のようになります。

python3

1rule = { 2 "STUDENT" : [["生徒"], ["学生"]], 3 "STUDENTS" : [["STUDENT", "たち"], ["STUDENT", "ら"]], 4 "TEACHER" : [["先生"], ["教師"], ["教員"]], 5 "TEACHERS" : [["TEACHER", "たち"], ["TEACHER", "ら"]], 6 "FRUIT_EAT" : [["リンゴ", "を", "食べた"], ["ミカン", "を", "食べた"]] 7} 8a = ["生徒", "が", "リンゴ", "を", "食べた"] 9b = ["教師", "たち", "は", "ミカン", "を", "食べた"] 10c = ["教員", "と", "学生", "ら", "は", "ブドウ", "を", "食べた"] 11 12l = [a, b, c] 13 14l = [replace_sentence(sent, rule) for sent in l] 15 16print(l) 17

私の手元の Python 3.7.3 で実行すると、結果が以下のように出力されました。

[['STUDENT', 'が', 'FRUIT_EAT'], ['TEACHERS', 'は', 'FRUIT_EAT'], ['TEACHER', 'と', 'STUDENTS', 'は', 'ブドウ', 'を', '食べた']]

追記1

質問者様はすでにご承知かとは思いますが、以下、念のための補足です。

ルールの
"STUDENT" : [["生徒"], ["学生"]]
は、
"STUDENTS" : [["STUDENT", "たち"], ["STUDENT", "ら"]]
よりも先に適用される必要があるので、ルールのdict rule は、ソースに書いたとおりに順番が保存されている必要があります。そのため実行には、dictのエントリの順番が保存されるようになった Python のバージョン 3.7以上が必要です。

追記2

Repl.it に実行確認用のコードを上げました。

ただし、 使われているPython のバージョンが現時点では 3.6 だったので、 rule は単なるdictではなくOrderedDict を使って、適用されるルールの順番が意図通りになるようにしています。

追記3

関数を作らずに、リスト内包表記の中にすべてを詰め込んでみます。

先の回答で作成した関数 replace_sentence(sent, rule) では、全てのルールを順に適用させていくためにfor ループを使っていますが、これを reduceで実現します。そうすると、各ルールごとの変換処理は、 reduce の第1引数に lambda として書くことができ、以下のようになります。

python3

1import re 2from functools import reduce 3 4 5l = [ 6 reduce( 7 lambda s, r: re.sub('|'.join(['/'.join(e) for e in r[1]]), r[0], '/'.join(s)).split('/'), 8 rule.items(), 9 sent 10 ) for sent in l 11]

投稿2019/05/24 11:40

編集2019/05/26 11:45
jun68ykt

総合スコア9058

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

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

k_k_

2019/05/24 13:31

jun68ykt様、 丁寧なご回答ありがとうございます。 正規表現はややこしいイメージがあったので、避けていたのですが、回答者様の方法ならわかりやすくていいですね。参考にさせていただきます。 辞書の順番については、分かっていたつもりでしたが、実装するのを忘れていました。 ご指摘ありがとうございます。
jun68ykt

2019/05/24 13:47

どういたしまして。参考になりましたら幸いです。
jun68ykt

2019/05/24 23:30

reduce を使って、リスト内包表記だけで変換するコードを、回答のほうに追記3として書きました。ご参考まで。
k_k_

2019/05/25 02:58

jun68ykt様、 追加の回答ありがとうございます。 回答について質問をしたいのですが、 rule.items()のキーと値がそれぞれ、lambdaのs,rになりますよね? "TEACHER" : [["先生"], ["教師"], ["教員"]] のようにrの値は3以上になることもあります。 そうなると、r[0]やr[1]の挙動がよくわからないのです。 私が、reduceの動作を理解しきれていないのもあるので、解説があれば嬉しいです。
k_k_

2019/05/25 03:56

すみません。勘違いしていたみたいで、自己解決しました。
jun68ykt

2019/05/25 04:13

こんにちは。解決されたとのことで、よかったです ????
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問