简体   繁体   中英

How to get the closest value in a list?

I have a variable which looks like this: my_value = 188 and I have a list which looks like this:

my_list = [
    ['Morocco', 'Meat', '190,00', '0,15'], 
    ['Morocco', 'Meat', '189,90', '0,32'], 
    ['Morocco', 'Meat', '189,38', '0,44'],
    ['Morocco', 'Meat', '188,94', '0,60'],
    ['Morocco', 'Meat', '188,49', '0,78'],
    ['Morocco', 'Meat', '187,99', '0,101'],
    ['Spain', 'Meat', '190,76', '0,10'], 
    ['Spain', 'Meat', '190,16', '0,20'], 
    ['Spain', 'Meat', '189,56', '0,35'],
    ['Spain', 'Meat', '189,01', '0,40'],
    ['Spain', 'Meat', '188,13', '0,75'],
    ['Spain', 'Meat', '187,95', '0,85'],
    ['Italy', 'Meat', '190,20', '0,11'],
    ['Italy', 'Meat', '190,10', '0,31'], 
    ['Italy', 'Meat', '189,32', '0,45'],
    ['Italy', 'Meat', '188,61', '0,67'],
    ['Italy', 'Meat', '188,01', '0,72'],
    ['Italy', 'Meat', '187,36', '0,80']]

As you can see in my_list the numbers in index [2] are descending while the numbers in index [3] are ascending. Now for every list I want to check at what index [3] is index [2] the closest to my_value BUT it should not fall below my_value.

I tried below code:

for key,sublists in itertools.groupby(my_list,lambda y:y[0]):
        v=[] #initialize it in case no element fulfill the condition
        for v in itertools.takewhile(lambda x:float(x[-1].replace(",","."))<my_value ,sublists):
            pass
        if v: 
            print(v[-1])

I received the following output:

0,101
0,85
0,80

The output I want is:

0,78
0,75
0,72
my_value = 188
numbers = [float(line[2].replace(',', '.')) for line in my_list]
minima = [(num - my_value, line[3]) for num, line in zip(numbers, my_list) if num >= my_value]
minima = sorted(minima, key=lambda x: x[0])
top3 = [val[1] for val in minima[:3]]
print(top3)

First I create a list numbers which holds the values of index 2 of each list in my_list converted to a float, so we are actually able to perform calculations on these numbers. This is done using a list comprehension.

Then I create a new list minima where I calculate the difference between my_value and all values in numbers , with the condition that the value in numbers has to be greater than or equal to my_number . By using zip(number, my_list) I simultaneously loop of both the list numbers and my_list so that I can put together the differense num - myvalue together with the respective value in index 3 of my_list into a tuple.

The list minima then looks like this:

[(2.0, '0,15'),
 (1.9000000000000057, '0,32'),
 (1.3799999999999955, '0,44'),
 (0.9399999999999977, '0,60'),
 (0.4900000000000091, '0,78'),
 (2.759999999999991, '0,10'),
 (2.1599999999999966, '0,20'),
 (1.5600000000000023, '0,35'),
 (1.009999999999991, '0,40'),
 (0.12999999999999545, '0,75'),
 (2.1999999999999886, '0,11'),
 (2.0999999999999943, '0,31'),
 (1.3199999999999932, '0,45'),
 (0.6100000000000136, '0,67'),
 (0.009999999999990905, '0,72')]

Then I sort that list in ascending order by its first value in the tuple which is the difference between my_value and the numbers in index 3. I then take the first three and return just the second value in the tuple.

Just on a sidenote: If you are dealing with tabular data like this, it might be a good idea to look into the pandas library. Another answer on this post shows a way to do this with pandas. It can often be easier and more user friendly to deal with tabular data with that library because it offers alot of functionality to easily perform alot of data manipulation and analysis.

This is the kind of work where pandas comes to the rescue:

import pandas as pd
my_value = 188
my_list = [] # complete this with your list above

df = pd.DataFrame(my_list) # make a DataFrame out of your list
df[2] = df[2].str.replace(",", ".").astype(float) # convert those strings to actual floats
df[3] = df[3].str.replace(",", ".").astype(float)
selected = df[df[2]>my_value].groupby(by=0).agg({2:'min',3:'last'}) # selects what you want

print(list(selected[3])) # if you just want those values

will output

[0.72, 0.78, 0.75]

selected will look like:

0       2       3
Italy   188.01  0.72
Morocco 188.49  0.78
Spain   188.13  0.75

Why not use the built-in sort()?:


my_list = [row for row in my_list if float(row[2].replace(',','.')) >= my_value]
my_list.sort(key=lambda l:float(l[2].replace(',','.')))

for row in my_list:
    print(row)

>

['Italy', 'Meat', '188,01', '0,72']
['Spain', 'Meat', '188,13', '0,75']
['Morocco', 'Meat', '188,49', '0,78']
['Italy', 'Meat', '188,61', '0,67']
['Morocco', 'Meat', '188,94', '0,60']
['Spain', 'Meat', '189,01', '0,40']
['Italy', 'Meat', '189,32', '0,45']
['Morocco', 'Meat', '189,38', '0,44']
['Spain', 'Meat', '189,56', '0,35']
['Morocco', 'Meat', '189,90', '0,32']
['Morocco', 'Meat', '190,00', '0,15']
['Italy', 'Meat', '190,10', '0,31']
['Spain', 'Meat', '190,16', '0,20']
['Italy', 'Meat', '190,20', '0,11']
['Spain', 'Meat', '190,76', '0,10']

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM