Python Return closest number above and below given x - TagMerge
4Python Return closest number above and below given xPython Return closest number above and below given x

Python Return closest number above and below given x

Asked 5 months ago
0
4 answers

import bisect

def find_nearest(arr,value):
    arr.sort()
    idx = bisect.bisect_left(arr, value)

    if idx == 0:
        return None, arr[idx]
    elif idx == len(arr):
        return arr[idx - 1], None
    elif arr[idx] == value:
        return arr[idx], arr[idx]
    else:
        return arr[idx - 1], arr[idx]

array = [1, 5, 7, 9, 10, 11, 14, 15]

print(find_nearest(array, 0))
print(find_nearest(array, 4))
print(find_nearest(array, 8))
print(find_nearest(array, 10))
print(find_nearest(array, 20))

Output:

(None, 1)
(1, 5)
(7, 9)
(10, 10)
(15, None)

Helper source

Source: link

0

Assuming that your array is not sorted, you will be indeed forced to scan the entire array. But if it is sorted you can use a binary search approach that will run in O(log(n)), something like this:

def search_nearest_elems(array, elem):
    """
    Uses dichotomic search approach.
    Runs in O(log(n)), with n = len(array)
    """
    i = len(array) // 2
    left, right = 0, len(array) - 1
    while right - left > 1:
        if array[i] == elem:
            return elem, elem
        elif array[i] > elem:
            right, i = i, (left + i) // 2
        else:
            left, i = i, (right + i) // 2

    return array[left], array[right]

array = [1, 5, 7, 9, 10, 11, 14, 15]
assert search_nearest_elems(array, 8) == (7, 9)
assert search_nearest_elems(array, 9) == (9, 9)
assert search_nearest_elems(array, 14.5) == (14, 15)
assert search_nearest_elems(array, 2) == (1, 5)

Source: link

0

This answer based on @Mark Tolonen hint about bisect

Let's say that you need to append one number on every iteration but initial array is the same.
All elements in array should be unique. Initial array may be not sorted.

import bisect as b


init_array = [5, 9, 10, 11, 14, 15, 1, 7]

init_array.sort()

numbers_to_append = [22, 4, 12, 88, 99, 109, 999, 1000, 1001]

numbers_to_check = [12, 55, 23, 55, 0, 55, 10, 999]

for (_, n) in enumerate(numbers_to_check):

  # get index of closer right to below
  i = b.bisect_left(init_array, n)

  # get above
  if i >= (len(init_array)):
    above = None
  else:
    if init_array[i] == n:
      try:
        above = init_array[i + 1]
      except IndexError:
        above = None
    else:
      above = init_array[i]

  # get below
  below = init_array[i - 1] if i - 1 >= 0 and init_array[i - 1] - n < 0 else None

  print('---------')
  print('array is', init_array)
  print('given x is', n)
  print('below is', below)
  print('above is', above)
  print('appending', numbers_to_append[_])

  # check if number trying to append doesn't exists in array
  # WARNING: this check may be slow and added only for showing that we need to add only unique numbers
  # real check should be based on real conditions and numbers that we suppose to add
  i_to_append = b.bisect_left(init_array, numbers_to_append[_])
  if numbers_to_append[_] not in init_array[i_to_append:i_to_append+1]:
    init_array.insert(i_to_append, numbers_to_append[_])

output:

---------
array is [1, 5, 7, 9, 10, 11, 14, 15]
given x is 12
below is 11
above is 14
appending 22
---------
array is [1, 5, 7, 9, 10, 11, 14, 15, 22]
given x is 55
below is 22
above is None
appending 4
---------
array is [1, 4, 5, 7, 9, 10, 11, 14, 15, 22]
given x is 23
below is 22
above is None
appending 12
---------
array is [1, 4, 5, 7, 9, 10, 11, 12, 14, 15, 22]
given x is 55
below is 22
above is None
appending 88
---------
array is [1, 4, 5, 7, 9, 10, 11, 12, 14, 15, 22, 88]
given x is 0
below is None
above is 1
appending 99
---------
array is [1, 4, 5, 7, 9, 10, 11, 12, 14, 15, 22, 88, 99]
given x is 55
below is 22
above is 88
appending 109
---------
array is [1, 4, 5, 7, 9, 10, 11, 12, 14, 15, 22, 88, 99, 109]
given x is 10
below is 9
above is 11
appending 999
---------
array is [1, 4, 5, 7, 9, 10, 11, 12, 14, 15, 22, 88, 99, 109, 999]
given x is 999
below is 109
above is None
appending 1000

Source: link

0

I have a DataFrame that contains the data shown below:
soc [%]  r0 [ohm]     tau1 [s]   tau2 [s]  r1 [ohm]  r2 [ohm]     c1 [farad]    c2 [farad]
0       90  0.001539  1725.035378  54.339882  0.001726  0.001614  999309.883552  33667.261120
1       80  0.001385   389.753276  69.807148  0.001314  0.001656  296728.345634  42164.808208
2       70  0.001539   492.320311  53.697439  0.001139  0.001347  432184.454388  39865.959637
3       60  0.001539   656.942558  63.233445  0.000990  0.001515  663400.436465  41727.472274
4       50  0.001539   296.080424  53.948112  0.000918  0.001535  322490.860387  35139.878909
5       40  0.001539   501.978979  72.015509  0.001361  0.001890  368919.408585  38100.665763
6       30  0.001539   585.297624  76.972464  0.001080  0.001872  542060.285388  41114.220492
7       20  0.001385  1308.176576  60.541172  0.001426  0.001799  917348.863136  33659.124096
8       10  0.001539  1194.993755  57.078336  0.002747  0.001851  435028.073957  30839.130201
Given a value z, I want to select a row in the data frame where soc [%] is closest to z. The code below demonstrates my current approach.
import pandas as pd
import time


def rc_params(df, z):

    if z > 90:
        params = df.loc[0]
    elif 80 < z <= 90:
        params = df.loc[0]
    elif 70 < z <= 80:
        params = df.loc[1]
    elif 60 < z <= 70:
        params = df.loc[2]
    elif 50 < z <= 60:
        params = df.loc[3]
    elif 40 < z <= 50:
        params = df.loc[4]
    elif 30 < z <= 40:
        params = df.loc[5]
    elif 20 < z <= 30:
        params = df.loc[6]
    elif 10 < z <= 20:
        params = df.loc[7]
    else:
        params = df.loc[8]

    r0 = params['r0 [ohm]']
    tau1 = params['tau1 [s]']
    tau2 = params['tau2 [s]']
    r1 = params['r1 [ohm]']
    r2 = params['r2 [ohm]']

    return r0, tau1, tau2, r1, r2


start = time.time()

z = 20

df = pd.read_csv('results/soc_rc.csv')

r0, tau1, tau2, r1, r2 = rc_params(df, z)

end = time.time()

print(f"""
z = {z}
r0 = {r0:.4f}
tau1 = {tau1:.4f}
tau2 = {tau2:.4f}
r1 = {r1:.4f}
r2 = {r2:.4f}
run time = {end - start:.4g} s
""")
Results from the above code give:
z = 20
r0 = 0.0014
tau1 = 1308.1766
tau2 = 60.5412
r1 = 0.0014
r2 = 0.0018
run time = 0.002264 s
Not sure if this will help, but I'm using this to find nearest in a sorted column: (time series stuff)
result_index = df['col_to_search'].sub(search_value).abs().idxmin()
Adapting from here would be a cleaner way to do what you want.
params = df.iloc[(df['soc [%]']-z).abs().argsort()[:1]]

Source: link

Recent Questions on python

    Programming Languages