#python #list #if-statement #floating-point
#python #pandas #Список #if-оператор #с плавающей запятой
Вопрос:
У меня есть следующие 3 списка:
minimal_values = ['0,32', '0,35', '0,45']
maximal_values = ['0,78', '0,85', '0,72']
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,70'],
['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,55']]
Я пытаюсь фильтровать my_list
на основе, если index [-1]
находится между значением in minimal_values
и значением in maximal_values
.Эти значения соответствуют минимальным и максимальным значениям по странам. Я также выполняю вычитание внутри списка. Итак, для Марокко мне нужны только строки, где index[-1]
находится между 0,32
и т.д. 0,78
Проблема в том, что после 0,78
того, как значения уменьшатся 0,70
, это означает, что строка также удовлетворяет оператору if.
Примечание: значения в my_list
-1
сначала возрастают, а затем убывают. Мне нужны только строки в возрастающей части, а не в нисходящей части. Я не уверен, как решить эту проблему.
Это мой код:
price = 500
# Convert values to float.
minimal_values = [float(i.replace(',', '.')) for i in minimal_values]
maximal_values = [float(i.replace(',', '.')) for i in maximal_values]
# Collect all unique countries in a list.
countries = list(set(country[0] for country in my_list))
results = []
for l in my_list:
i = countries.index(l[0])
if minimal_values[i] <= float(l[-1].replace(',', '.')) <= maximal_values[i]:
new_index_2 = price - float(l[-2].replace(',', '.'))
l[-2] = new_index_2
results.append(l)
print(results)
Это мой текущий результат:
[['Morocco', 'Meat', '189.90', '0,32'],
['Morocco', 'Meat', 310.62, '0,44'],
['Morocco', 'Meat', 311.06, '0,60'],
['Morocco', 'Meat', 311.51, '0,78'],
['Morocco', 'Meat', 312.01, '0,70'],
['Spain', 'Meat', 310.44, '0,35'],
['Spain', 'Meat', 310.99, '0,40'],
['Spain', 'Meat', 311.87, '0,75'],
['Spain', 'Meat', '312.05', '0,85'],
['Italy', 'Meat', 310.68, '0,45'],
['Italy', 'Meat', 311.39, '0,67'],
['Italy', 'Meat', 311.99, '0,72'],
['Italy', 'Meat', 312.64, '0,55']]
Это мой желаемый результат:
[['Morocco', 'Meat', '189.90', '0,32'],
['Morocco', 'Meat', 310.62, '0,44'],
['Morocco', 'Meat', 311.06, '0,60'],
['Morocco', 'Meat', 311.51, '0,78'],
['Spain', 'Meat', 310.44, '0,35'],
['Spain', 'Meat', 310.99, '0,40'],
['Spain', 'Meat', 311.87, '0,75'],
['Spain', 'Meat', '312.05', '0,85'],
['Italy', 'Meat', 310.68, '0,45'],
['Italy', 'Meat', 311.39, '0,67'],
['Italy', 'Meat', 311.99, '0,72']]
***** Ответы, связанные с Pandas, также приветствуются.
Комментарии:
1. Это кажется тихим интересным случаем. Мне любопытно узнать, как это можно решить.
2. Да, я тоже.. тихий сложный, хотя я думаю.
3. Рассматривали ли вы возможность использования pandas для этой задачи (и, вероятно, многих других следующих задач, подобных этой …), Для этого типа задач есть встроенные, гораздо более интуитивно понятные и простые операции…
4. Я не уверен, как использовать padnas специально для этого.
5. добавьте к вопросу, что вы принимаете решения, связанные с pandas.
Ответ №1:
решение pandas:
import pandas as pd
import numpy as np
# create input dataframe
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,70'],
['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,55']]
dfi = pd.DataFrame(my_list).applymap(lambda x: x.replace(',', '.'))
dfi[[2, 3]] = dfi[[2, 3]].astype(float)
print(dfi)
# 0 1 2 3
# 0 Morocco Meat 190.00 0.15
# 1 Morocco Meat 189.90 0.32
# 2 Morocco Meat 189.38 0.44
# 3 Morocco Meat 188.94 0.60
# 4 Morocco Meat 188.49 0.78
# 5 Morocco Meat 187.99 0.70
# 6 Spain Meat 190.76 0.10
# 7 Spain Meat 190.16 0.20
# 8 Spain Meat 189.56 0.35
# 9 Spain Meat 189.01 0.40
# 10 Spain Meat 188.13 0.75
# 11 Spain Meat 187.95 0.85
# 12 Italy Meat 190.20 0.11
# 13 Italy Meat 190.10 0.31
# 14 Italy Meat 189.32 0.45
# 15 Italy Meat 188.61 0.67
# 16 Italy Meat 188.01 0.72
# 17 Italy Meat 187.36 0.55
# create df_filter with contry and min_v, max_v
minimal_values = ['0,32', '0,35', '0,45']
maximal_values = ['0,78', '0,85', '0,72']
minimal_values = [float(i.replace(',', '.')) for i in minimal_values]
maximal_values = [float(i.replace(',', '.')) for i in maximal_values]
df_filter = pd.DataFrame(list(zip(dfi[0].unique().tolist(),
minimal_values,
maximal_values)))
df_filter.columns = [0, 'min_v', 'max_v']
print(df_filter)
# 0 min_v max_v
# 0 Morocco 0.32 0.78
# 1 Spain 0.35 0.85
# 2 Italy 0.45 0.72
# merge dfi and fi_filter
dfm = pd.merge(dfi, df_filter, on=0, how='left')
print(dfm)
# 0 1 2 3 min_v max_v
# 0 Morocco Meat 190.00 0.15 0.32 0.78
# 1 Morocco Meat 189.90 0.32 0.32 0.78
# 2 Morocco Meat 189.38 0.44 0.32 0.78
# 3 Morocco Meat 188.94 0.60 0.32 0.78
# 4 Morocco Meat 188.49 0.78 0.32 0.78
# 5 Morocco Meat 187.99 0.70 0.32 0.78
# 6 Spain Meat 190.76 0.10 0.35 0.85
# 7 Spain Meat 190.16 0.20 0.35 0.85
# 8 Spain Meat 189.56 0.35 0.35 0.85
# 9 Spain Meat 189.01 0.40 0.35 0.85
# 10 Spain Meat 188.13 0.75 0.35 0.85
# 11 Spain Meat 187.95 0.85 0.35 0.85
# 12 Italy Meat 190.20 0.11 0.45 0.72
# 13 Italy Meat 190.10 0.31 0.45 0.72
# 14 Italy Meat 189.32 0.45 0.45 0.72
# 15 Italy Meat 188.61 0.67 0.45 0.72
# 16 Italy Meat 188.01 0.72 0.45 0.72
# 17 Italy Meat 187.36 0.55 0.45 0.72
# filter min_v <= column 3 <= max_v
cond = dfm[3].ge(dfm.min_v) amp; dfm[3].le(dfm.max_v)
dfm = dfm[cond].copy()
# filter 3 that is not ascending
cond = dfm.groupby(0)[3].diff() < 0
dfo = dfm.loc[~cond, [0,1,2,3]].reset_index(drop=True)
# outut result
price = 500
dfo[2] = price - dfo[2]
print(dfo)
# 0 1 2 3
# 0 Morocco Meat 310.10 0.32
# 1 Morocco Meat 310.62 0.44
# 2 Morocco Meat 311.06 0.60
# 3 Morocco Meat 311.51 0.78
# 4 Spain Meat 310.44 0.35
# 5 Spain Meat 310.99 0.40
# 6 Spain Meat 311.87 0.75
# 7 Spain Meat 312.05 0.85
# 8 Italy Meat 310.68 0.45
# 9 Italy Meat 311.39 0.67
# 10 Italy Meat 311.99 0.72
Комментарии:
1. Этот код выполняется примерно в 1000 раз медленнее , чем простой цикл в моем ответе. ideone.com/EG4SNd
2.
dfi['num2'] = dfi['num2'].str.replace('0,','').astype(int); dfi['num1'] = dfi['num1'].str.replace(r',','.').astype(float)
вместо функции apply … ускорит ли это,3. Вы правы! Это медленнее, но проще. Это просто альтернативный способ.
4. @adirabargil нет, импорт выполняется в той
setup
части кода, которая выполняется только один раз и не учитывается во времени. Из руководства : «Время выполнения setup исключается из общего времени выполнения»
Ответ №2:
Обратите внимание, что у вас есть проблема в вашем коде в том, что порядок элементов countries
не обязательно совпадает с порядком стран в my_list
. Проще просто обрабатывать страны по мере обработки списка, делая пометку при изменении названия страны. Затем вы можете добавить флаг в свой цикл, который указывает, что обработка для этой страны завершена (когда текущее значение меньше предыдущего значения), и если это так, игнорируйте оставшиеся значения для этой страны:
# Convert values to float.
minimal_values = [float(i.replace(',', '.')) for i in minimal_values]
maximal_values = [float(i.replace(',', '.')) for i in maximal_values]
# Collect all unique countries in a list.
results = []
finished_country = -1
country_index = -1
last_country = ''
for l in my_list:
country = l[0]
if country != last_country:
country_index = 1
last_country = country
value = float(l[-1].replace(',', '.'))
if finished_country == country_index or value < minimal_values[country_index]:
last_value = 0
continue
if value < last_value:
finished_country = country_index
elif value <= maximal_values[country_index]:
new_index_2 = price - float(l[-2].replace(',', '.'))
l[-2] = new_index_2
results.append(l)
last_value = value
Вывод для ваших выборочных данных:
[
['Morocco', 'Meat', 310.1, '0,32'],
['Morocco', 'Meat', 310.62, '0,44'],
['Morocco', 'Meat', 311.06, '0,60'],
['Morocco', 'Meat', 311.51, '0,78'],
['Spain', 'Meat', 310.44, '0,35'],
['Spain', 'Meat', 310.99, '0,40'],
['Spain', 'Meat', 311.87, '0,75'],
['Spain', 'Meat', 312.05, '0,85'],
['Italy', 'Meat', 310.68, '0,45'],
['Italy', 'Meat', 311.39, '0,67'],
['Italy', 'Meat', 311.99, '0,72']
]
Комментарии:
1.
TypeError: '<' not supported between instances of 'float' and 'str'
2. @TangerCity вы удалили строки, преобразующие эти списки в float?
3. Ах, вот оно что. Позвольте мне проверить это.
Ответ №3:
minimal_values = [float(i.replace(',', '.')) for i in minimal_values]
maximal_values = [float(i.replace(',', '.')) for i in maximal_values]
countries_largest = {}
filtered_list = []
for row in my_list:
country_name = row[0]
value = float(row[-1].replace(',','.'))
if country_name in countries_largest and value < countries_largest[country_name]:
continue
countries_largest[country_name] = value
if not (minimal_values[len(countries_largest)-1] <= value <= maximal_values[len(countries_largest)-1]):
continue
filtered_list.append(row)
[['Morocco', 'Meat', '189,90', '0,32'],
['Morocco', 'Meat', '189,38', '0,44'],
['Morocco', 'Meat', '188,94', '0,60'],
['Morocco', 'Meat', '188,49', '0,78'],
['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', '189,32', '0,45'],
['Italy', 'Meat', '188,61', '0,67'],
['Italy', 'Meat', '188,01', '0,72']]
Комментарии:
1. Ваш вывод не совпадает с моим. Я не хочу, чтобы были включены значения по убыванию.
2. О, я не совсем понял это. Я отредактирую его.
Ответ №4:
Учитывая:
minimal_values = ['0,32', '0,35', '0,45']
maximal_values = ['0,78', '0,85', '0,72']
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,70'],
['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,55']]
Во-первых, поскольку мы собираемся использовать его часто, давайте напишем небольшую процедуру преобразования, которая стандартизирует то, что мы подразумеваем под «плавающей точкой» в вашем случае:
def conv(s):
try:
return float(s.replace(',','.'))
except ValueError:
return s
Теперь кажется, что ваши два списка строк minimal_values
и maximal_values
являются сопоставлением с минимальными и максимальными значениями по странам. Если это так, ваше использование countries = list(set(country[0] for country in my_list))
не будет работать, поскольку наборы расположены в произвольном порядке во всех версиях Python.
Если у вас есть Python 3.6 , вы можете сделать:
countries = list({}.fromkeys(country[0] for country in my_list))
поскольку dicts сохраняют порядок вставки в Python 3.6 . Предполагая, что вы хотите что-то, что работает на всех версиях Python, вы можете вместо этого сделать:
def uniqs_in_order(li):
seen=set()
return [e for e in li if not (e in seen or seen.add(e))]
# Python 3.6 : return list({}.fromkeys(li))
Теперь вы можете создать отображение страны: кортеж минимального / максимального значения для этой страны:
mapping={k:(min_, max_) for k,min_,max_ in
zip(uniqs_in_order([sl[0] for sl in my_list]),
[conv(s) for s in minimal_values],
[conv(s) for s in maximal_values])}
>>> mapping
{'Morocco': (0.32, 0.78), 'Spain': (0.35, 0.85), 'Italy': (0.45, 0.72)}
Теперь, наконец, мы можем фильтровать. Поскольку вы хотите принимать только значения, которые:
- Находятся в пределах минимального и максимального значений по странам и;
- Остановка, когда значения по странам больше не возрастают.
Мы можем использовать groupby
from itertools, чтобы нарезать список списков по странам и выполнить эти два теста:
from itertools import groupby
filt=[]
price = 500
for k,v in groupby(my_list, key=lambda sl: sl[0]):
section=list(v)
for i, row in enumerate(section):
if i and conv(row[-1])<conv(section[i-1][-1]):
break
if mapping[row[0]][0]<=conv(row[-1])<=mapping[row[0]][1]:
row[-2]=price-conv(row[-2])
filt.append(row)
>>> filt
[['Morocco', 'Meat', 310.1, '0,32'],
['Morocco', 'Meat', 310.62, '0,44'],
['Morocco', 'Meat', 311.06, '0,60'],
['Morocco', 'Meat', 311.51, '0,78'],
['Spain', 'Meat', 310.44, '0,35'],
['Spain', 'Meat', 310.99, '0,40'],
['Spain', 'Meat', 311.87, '0,75'],
['Spain', 'Meat', 312.05, '0,85'],
['Italy', 'Meat', 310.68, '0,45'],
['Italy', 'Meat', 311.39, '0,67'],
['Italy', 'Meat', 311.99, '0,72']]