意外と詰まったし良いまとめがないので書く.
#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 まとめ
添字取り出し,意外と大変だったけど,いつかどこかの誰かの役に立てれば.