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

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

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

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

Q&A

解決済

4回答

2631閲覧

二次元リスト内の各要素を比較したい

退会済みユーザー

退会済みユーザー

総合スコア0

Python 3.x

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

0グッド

0クリップ

投稿2021/08/23 14:12

編集2021/08/23 14:15

前提・実現したいこと

N行N列の二次元リストの各要素を比較し、上下左右の要素より大きい要素を出力したいです。
上下左右に要素がない場合は、その方向より大きかったとみなします。

発生している問題・エラーメッセージ

エラーメッセージは解決したものの、4/10ケースで出力結果が間違っています。

教えて頂きたいこと

より簡単に、エラーを起こしにくい書き方があれば教えて頂きたいです。
以下のコードは上下左右の要素と比較する関数を定義し、インデックスの値を変化させながら関数を実行しています。比較して大きかった場合のみカウンタを+1し、カウンタが4になった(=上下左右の要素より大きかった)要素をリストに格納しました。
エラーを解決しながらコードを書き、100分近くかかってしまっています。
それ自体は勉強になって良いのですが、より良い他の書き方が思いつきません。

該当のソースコード

x = 0 y = 0 # 以降のaは2次元リストで、全ての要素はint型です def r(a): # 右の要素と比較 if y + 1 < N: return a[x][y] - a[x][y + 1] else: return a[x][y] def l(a): # 左の要素と比較 if 0 < y: return a[x][y] - a[x][y - 1] else: return a[x][y] def t(a): # 上の要素と比較 if x - 1 > 0: return a[x][y] - a[x - 1][y] else: return a[x][y] def u(a): # 下の要素と比較 if x + 1 < N: return a[x][y] - a[x + 1][y] else: return a[x][y] while y < N: while x < N: count = 0 cul = r(a) if cul > 0: count +=1 cul = l(a) if cul > 0: count +=1 cul = t(a) if cul > 0: count +=1 cul = u(a) if cul > 0: count += 1 if count == 4: high.append(a[x][y]) x += 1 x = 0 y += 1

どんな細かい点でもご指摘頂けると幸いです。

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

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

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

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

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

guest

回答4

0

scipy.ndimage.maximum_filter()を使うと任意の範囲の中の最大値を算出してくれます。

python

1import numpy as np 2from scipy import ndimage 3 4A = np.array([[3, -9, 10, 6], 5 [2, 6, -3, -8], 6 [9, -7, 7, -8]]) 7 8FP = [[0, 1, 0], 9 [1, 1, 1], 10 [0, 1, 0]] 11 12filtered = ndimage.maximum_filter(A, footprint=FP) 13print(filtered) 14# [[ 3 10 10 10] 15# [ 9 6 10 6] 16# [ 9 9 7 7]] 17 18out = A[ndimage.maximum_filter(A, footprint=FP) == A] 19 20print(out) # -> [ 3 10 6 9 7]

投稿2021/08/24 02:29

編集2021/08/25 01:07
kirara0048

総合スコア1399

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

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

退会済みユーザー

退会済みユーザー

2021/08/24 12:52

ご回答ありがとうございます!そんな便利なものが有るのですね、まだscipyには手を出せていなかったので勉強になります。以下、理解が合っているか確認して頂けるととても助かります。 引数のfootprintは、 ・FPのように3行3列のnumpy配列で構成されるもので、要素は0または1 ・入力配列の各要素に対して、FP[1,1]を各要素とし、1としたところを範囲とする ・入力配列の各要素が任意の範囲をとれない(A[0,0]で上と左の要素が存在しない等)場合でも、範囲をとれた部分(A[0,0]なら右と下)のみを範囲とする また、out = A[]とすることで、bool型がTrueであるAの要素をoutに代入していることは分かったのですが、[]の最後の == Aは何を意味しているのか分かりません。お時間がございましたら教えていただけますか?よろしくお願いいたします。
kirara0048

2021/08/25 01:05

この十字のFPでmaximum_filter()を使うと、「Aの各値が『自身・上隣・下隣・左隣・右隣のうち、最も大きい値』に変換された配列」が返されます。 今回、取り出す条件が「上下左右と比較して大きい値」ですから、つまり「自身・上隣・下隣・左隣・右隣のうち、最も大きい値」が「自身の値」と一致しているものを取りせばよいので、`.maximum_filter(~~) == A`を行ってインデックスを取得しています。
退会済みユーザー

退会済みユーザー

2021/08/25 08:43

丁寧で分かりやすい説明をありがとうございます。 また、コードも編集してくださったのでより理解しやすくなりました。 使える知識となるよう復習いたします。 この度はありがとうございました!
guest

0

ベストアンサー

自分ならこうやりますかの〜ゆうん書いときますう。
最初にゆうときますけど、ワテ、便利なものは使いまくって楽したい派なんで、NumPy 使わせてもらいますわ。

それから問題のとらえ方やけど、たとえば、M=3、N=4として、3行4列の行列(に相当する二次元リスト)A

python

1[ 2 [ 3, -9, 10, 6], 3 [ 2, 6, -3, -8], 4 [ 9, -7, 7, -8] 5]

というものであったとして、Aから以下のような、同じく3行4列の行列Bを得る問題として考えることにするで。

python

1[ 2 [True, False, True, False], 3 [False, True, False, False], 4 [True, False, True, False] 5]

つまり、Bの要素はbool値で、Aの同じ位置の要素が題意を満たしていればTrue、満たしていなければFalse になっとるゆう行列ですわ。こないなBAから作るコードとして、こんなん思いついたで。

python

1import numpy as np 2 3# 行および列の数 4M, N = 3, 4 5 6# M行N列の二次元配列Aを作る。 7A = np.array([ 8 [ 3, -9, 10, 6], 9 [ 2, 6, -3, -8], 10 [ 9, -7, 7, -8] 11]) 12 13 14# 一次元配列 ary を受け取り、aryの各要素が、先頭要素であるか、もしくは左(ひとつ前)の要素より大きいかを示すブール値の配列を返す関数 15def check_left(ary): 16 return [i == 0 or x > ary[i-1] for (i, x) in enumerate(ary)] 17 18# 一次元配列 ary を受け取り、aryの各要素が、末尾の要素であるか、もしくは右(次)の要素より大きいかを示すブール値の配列を返す関数 19def check_right(ary): 20 return [i == len(ary)-1 or x > ary[i+1] for (i, x) in enumerate(ary)] 21 22 23# Bの初期値を設定(値がすべてTrueのM×N行列) 24B = np.ones((M, N), dtype=bool) 25 26# Aに対して、行方向および列方向それぞれに、check_leftおよびcheck_rightを適用した行列Cを得て、BとCの論理積を、Bに代入するループ 27for axis in [0, 1]: 28 for checker in [check_left, check_right]: 29 C = np.apply_along_axis(checker, axis, A) 30 B = np.logical_and(B, C) 31 32 33# 結果の出力 34print(B) 35print() 36 37for i, row in enumerate(B): 38 for j, check in enumerate(row): 39 if check: 40 print(f'A({i}, {j}): ', A[i,j]) 41

上のコード実行すると、

[[ True False True False]

[False True False False]
[ True False True False]]

A(0, 0): 3

A(0, 2): 10
A(1, 1): 6
A(2, 0): 9
A(2, 2): 7

ゆう出力になりますわ。ほな〜。

補足: まあ、ゆうてNumPy使わんでも、

python

1def check(i, j, a): 2 return ( 3 (i == 0 or a[i][j] > a[i-1][j]) 4 and 5 (i == M-1 or a[i][j] > a[i+1][j]) 6 and 7 (j == 0 or a[i][j] > a[i][j-1]) 8 and 9 (j == N-1 or a[i][j] > a[i][j+1]) 10 ) 11 12 13for i in range(M): 14 for j in range(N): 15 if check(i, j, A): 16 print(f'A({i}, {j}): ', A[i][j]) 17

でもええわいね。

投稿2021/08/24 01:41

編集2021/08/24 03:24
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

退会済みユーザー

退会済みユーザー

2021/08/24 14:05

ご回答ありがとうございます!NumPyを使わないコードは理解できました。題意を満たすか判定したbool値のリストを得るという問題の捉え方をすれば、これだけすっきりしたコードが書けるのですね! NumPyのコードの以下の部分があまり理解できなかったので、もう少し教えて頂けませんか? # Aに対して、行方向および列方向それぞれに、check_leftおよびcheck_rightを適用した行列Cを得て、BとCの論理積を、Bに代入するループ for axis in [0, 1]: #? 行(0)、列(1)方向の順に処理を行・列単位で行わせる? for checker in [check_left, check_right]: C = np.apply_along_axis(checker, axis, A) #? Aに対して、check_leftおよびcheck_rightを適用したbool値checkerを行・列単位で行列Cに代入? 引数axisの意味するものは? B = np.logical_and(B, C) #? Cを出力してみたのですが、何ができたのか理解できません、ですので値が全てTrueのBとの論理積を取る意味も理解できませんでした また、for x in の最後にはfor下の処理を適用したい範囲を書くものと思っていたので、[0,1]や[check_left, check_right]が書かれていると何をしているのか分からないです。 質問攻めにして申し訳ございません。お時間がございましたらご回答頂けると大変嬉しく思います。よろしくお願いいたします。
guest

0

機能(関数)分割がうまくないなと感じました。
最終的には suwmn50799さんのコードでみんな納得するのではないでしょうか。

さて、とりあえず分割を見直したコード

python

1N = 3 # 1..30 2# 以降のaは2次元リストで、全ての要素はint型です (1..1000) 3a = [ 4 [1,2,3], 5 [4,5,6], 6 [7,8,9] 7] 8high = [] 9 10def check4(a,x,y): 11 count = 0 12 # 右の要素と比較 13 if y + 1 < N: 14 cul = a[x][y] - a[x][y + 1] 15 else: 16 cul = a[x][y] 17 if cul > 0: 18 count += 1 19 # 左の要素と比較 20 if 0 < y: 21 cul = a[x][y] - a[x][y - 1] 22 else: 23 cul = a[x][y] 24 if cul > 0: 25 count += 1 26 # 上の要素と比較 27 if x - 1 > 0: 28 cul = a[x][y] - a[x - 1][y] 29 else: 30 cul = a[x][y] 31 if cul > 0: 32 count +=1 33 # 下の要素と比較 34 if x + 1 < N: 35 cul = a[x][y] - a[x + 1][y] 36 else: 37 cul = a[x][y] 38 if cul > 0: 39 count += 1 40 return count 41 42x = 0 43y = 0 44while y < N: 45 while x < N: 46 if check4(a,x,y) == 4: 47 high.append(a[x][y]) 48 x += 1 49 x = 0 50 y += 1 51print(high)

で、最終的に欲しいのは(countでなく)比較結果なので

python

1N = 3 # 1..30 2# 以降のaは2次元リストで、全ての要素はint型です (1..1000) 3a = [ 4 [1,2,3], 5 [4,5,6], 6 [7,8,9] 7] 8high = [] 9 10def check4(a,x,y): 11 # 右の要素と比較 12 if y + 1 < N: 13 if a[x][y] <= a[x][y + 1]: 14 return False 15 # 左の要素と比較 16 if 0 < y: 17 if a[x][y] <= a[x][y - 1]: 18 return False 19 # 上の要素と比較 20 if x - 1 > 0: 21 if a[x][y] <= a[x - 1][y]: 22 return False 23 # 下の要素と比較 24 if x + 1 < N: 25 if a[x][y] <= a[x + 1][y]: 26 return False 27 return True 28 29x = 0 30y = 0 31while y < N: 32 while x < N: 33 if check4(a,x,y): 34 high.append(a[x][y]) 35 x += 1 36 x = 0 37 y += 1 38print(high)

ループも range() 使った方が手間がへりますね。

投稿2021/08/24 13:06

takasima20

総合スコア7460

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

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

退会済みユーザー

退会済みユーザー

2021/08/25 08:57

ご回答ありがとうございます。 他の方の回答にもあったように、条件との合致をbool値で判断するという考え方があれば、簡潔な書き方ができるのですね。 教えて頂いたコードで試してみたところ、4/10ケースで出力結果が間違っていたようです。おそらく私が前提条件をお伝えし損ねているからだと思いますので、再考してみます。 この度はありがとうございました!
guest

0

この書き方だと、全ての要素が負の整数である場合は一つも見つかりませんね。
それぞれの関数がTrueかFalseを返すようにすれば、このバグは修正できます。

投稿2021/08/23 15:09

ppaul

総合スコア24668

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

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

退会済みユーザー

退会済みユーザー

2021/08/24 03:39

ご回答ありがとうございます。 失礼しました。前提条件を忘れておりました。 ・1 ≦ N ≦ 30 ・1 ≦ 各要素の値 ≦ 1,000 したがって、ご指摘頂いたような要素が負であるためにより生じているエラーではないと考えております。 もし他に思いつくエラーの原因がございましたら教えて頂けますか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問