LoginSignup
8
12

More than 3 years have passed since last update.

【Python】 配列内で一番近い値を取る index を返す関数まとめ

Last updated at Posted at 2020-12-27

意外と詰まったし良いまとめがないので書く.

1 一番近い値の重複を考えない場合

1.1 最もシンプルなバージョン

探したい値が一つで,返したい index も一つの場合.これが最も簡単.探索されるデータは1次元配列を想定.

import numpy as np

def idx_of_the_nearest(data, value):
    idx = np.argmin(np.abs(np.array(data) - value))
    return idx

1.2 具体例

data = [1, 1 ,1 ,0.5 ,2 ,3 ,-1]
value =  0.8
n = idx_of_the_nearest(data, value)
n
#0

1.3 探したい値が複数ある場合

探したい値をリストで指定できるようにしたい.そのための関数が以下.

import numpy as np

def idx_of_the_nearest(data, value):
    print('value:', type(value))
    if type(value) == float:
        idx = np.argmin(np.abs(np.array(data) - value))
        #print(np.abs(np.array(data) - value))
        return idx
    if type(value) == list:
        idx = [None]*len(value)
        for i in range(len(value)):
            idx[i] = np.argmin(np.abs(np.array(data) - value[i]))
            #idx[i] = [value[i], np.argmin(np.abs(np.array(data) - value[i]))] #としてもよい
            #print(np.abs(np.array(data) - value[i]))
        return idx

1.4 具体例

data = [1, 1 ,1 ,0.5 ,2 ,3 ,-1]
value =  [0.8,0.7]
n = idx_of_the_nearest(data, value)
n
'''Result
value: <class 'list'>
[0, 3]
'''

1.5 多次元に拡張

探索される対象のデータが多次元配列の場合に,多次元配列の index を返したい人はこちら.出力はタプルのリスト.np.unravel_index()なるものを使う.

import numpy as np

def idx_of_the_nearest(data, value):
    print('value:', type(value))
    if type(value) == float:
        idx = np.argmin(np.abs(np.array(data) - value))
        #print(np.abs(np.array(data) - value))
        return idx
    if type(value) == list:
        idx = [None]*len(value)
        for i in range(len(value)):
            idx[i] = np.unravel_index(np.argmin(np.abs(np.array(data) - value[i])) , np.array(data).shape)
            #print(np.abs(np.array(data) - value[i]))
        return idx

1.6 具体例

data = [[1, 1 ,1 ,0.5] ,[2 ,3 ,-1,0]]
value = [0.8, 0.7, 2]
idx_of_the_nearest(data, value)
'''
value: <class 'list'>
[(0, 0), (0, 3), (1, 0)]
'''

2 最も近い値が複数ある場合

最もなのに「複数」とはどういうことだ,とは言わないでいただきたい.そういうことも数学ではままあるのである(今までもそういう具体例をわざと作っておいた.).

2.1 シンプルなバージョン

最もシンプルに添字を複数返したい方はこちら.1次元配列で探したい値が1つだけある場合.

import numpy as np

def indices_of_the_nearest(data, value):
    distance = np.abs(np.array(data) - value)
    indices = np.where(distance == np.min(distance))[0]
    return indices

この np.where() なるものが曲者で,意外と使いにくい.

ただ,シンプルなバージョンでは,具体例で見るようにこれが最適解である.[0]をつけることで arrayが返ってくる.

2.2 具体例

data = [1, 1 ,1 ,0.5 ,2 ,3 ,-1]
value = 0.8
indices_of_the_nearest(data, value)
'''
array([0, 1, 2])
'''

2.3 探したい値が複数ある場合(多次元でも使えるが改良の余地あり)

探す値も複数にしたいという「欲張り」な人はこちら.ただし改良の余地がある.

import numpy as np

def indices_of_the_nearest(data, value):
    print('value:', type(value))
    if type(value) == float:
        distance = np.abs(np.array(data) - value)
        indices = np.where(distance == np.min(distance))
        #print(np.abs(np.array(data) - value))
        return indices
    if type(value) == list:
        indices = [None]*len(value)
        for i in range(len(value)):
            distance = np.abs(np.array(data) - value[i])
            indices[i] = np.where(distance == np.min(distance))
            #print(np.abs(np.array(data) - value[i]))
        return indices

1.3 節を応用しただけ.

2.4 具体例

data = [[1, 1 ,1 ,0.5] ,[2 ,3 ,-1,0]]
value = [0.8,0.7]
indices_of_the_nearest(data, value)
'''
value: <class 'list'>
[(array([0, 0, 0]), array([0, 1, 2])), (array([0]), array([3]))]
'''

行番号と列番号が分離して出力されてわかりにくい(1つ目の要素の意味は[0][0]と[0][1]と[0][2]に一番近い値がありますよ,という意味).

どうしてもという人はこれを使えばよいが,今回の目的からしてあまり使う意味もない.data が1次元の場合は問題なかったのだが,これが更に3次元となってくるともっと見にくい.

data = [[[1, 1] ,[1 ,0.5]] ,[[2 ,3] ,[-1,0]]]
value = [0.8,0.7]
indices_of_the_nearest(data, value)
'''
value: <class 'list'>
[(array([0, 0, 0]), array([0, 0, 1]), array([0, 1, 0])),
 (array([0]), array([1]), array([1]))]
'''

リストの中の1つ目のタプルは[0][0][0], [0][0][1], [0][1][0] に一番近い値がありますよという意味.

これは array の中をそのまま読んでいるのでなくて,array([0, 0, 0])の i (i = 0, 1, 2) 番目とarray([0, 0, 1])の i 番目とarray([0, 1, 0])の i 番目をつなげて読んでいる(リストの中の2つ目のタプルを合わせて見れば意味がわかる).

見にくい!

2.5 多次元に拡張(改良版)

というわけで改良しよう.

import numpy as np

def indices_of_the_nearest(data, value):
    print('value:', type(value))
    if type(value) == float:
        distance = np.abs(np.array(data) - value)
        indices = np.array(np.where(distance == np.min(distance))).T
        #before:indices = np.where(distance == np.min(distance))
        #print(np.abs(np.array(data) - value))
        return indices
    if type(value) == list:
        indices = [None]*len(value)
        for i in range(len(value)):
            distance = np.abs(np.array(data) - value[i])
            indices[i] = np.array((np.where(distance == np.min(distance)))).T#Transpose
            #print(np.abs(np.array(data) - value[i]))
        return indices

何をやっているかというと,
indices[i] = np.array((np.where(distance == np.min(distance)))).T
と書き換えただけ.

(2/1 更新:1つ目の if 文の中
indices[i] = np.where(distance == np.min(distance))
だったところを,
indices[i] = np.array(np.where(distance == np.min(distance))).T
に変えました.これで1つ目の if 文の出力が2つ目の if 文の出力と統一されました.)

2.6 具体例

data = [[1, 1 ,1 ,0.5] ,[2 ,3 ,-1,0]]
value = [0.8,0.7]
indices_of_the_nearest(data, value)
'''
value: <class 'list'>
[array([[0, 0],
        [0, 1],
        [0, 2]]),
 array([[0, 3]])]
'''
data = [[[1, 1] ,[1 ,0.5]] ,[[2 ,3] ,[-1,0]]]
value = [0.8,0.7]
indices_of_the_nearest(data, value)
'''
value: <class 'list'>
[array([[0, 0, 0],
        [0, 0, 1],
        [0, 1, 0]]),
 array([[0, 1, 1]])]
'''

見やすい!(出力から要素を取り出したい場合は 内包表記で for 文を回すなどすればできると思う)

3 まとめ

添字取り出し,意外と大変だったけど,いつかどこかの誰かの役に立てれば.

8
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
12