Spaces:
Sleeping
Sleeping
File size: 17,973 Bytes
953bb94 b11dd33 81a31f1 953bb94 37f11f1 d7679bd 37f11f1 d7679bd 37f11f1 953bb94 fcfdbd9 eb906aa fb22448 eb906aa fb22448 f917e63 fb22448 eb906aa fb22448 fcfdbd9 eb906aa 32b314f eb906aa fcfdbd9 81a31f1 16e0d49 81a31f1 b11dd33 88f355f b11dd33 d7679bd b11dd33 d947247 d7679bd d947247 d7679bd d947247 b11dd33 543aebd b11dd33 a0279fa b11dd33 fc6d9f9 37f11f1 d7679bd 37f11f1 fc6d9f9 b11dd33 d7679bd 37f11f1 b11dd33 fc6d9f9 fcfdbd9 37f11f1 fc6d9f9 44e8610 fcfdbd9 44e8610 d947247 44e8610 fcfdbd9 44e8610 fcfdbd9 d947247 37f11f1 fcfdbd9 b11dd33 37f11f1 d7679bd fcfdbd9 b11dd33 d947247 37f11f1 88f355f 37f11f1 7b3dd41 ba6a100 7b3dd41 aa90b8e 7b3dd41 37f11f1 fc6d9f9 fa3d68e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | import gradio as gr
import joblib
import pandas as pd
import numpy as np
from geopy.distance import geodesic
import re
from sklearn.preprocessing import MultiLabelBinarizer
# Примерные данные для выпадающих списков
OPTIONS = {
# 'equipment': ['Не указано', 'Dynamic', 'Hit', 'Luxury', 'Burner', 'Invite+', 'Tiptronic', 'Value', 'Flagship', 'Kinetic', 'LTZ2V', 'Prime', 'LT+', 'Cayenne', 'GO!', 'XV', 'Feel', 'Премиум', 'Норма', '21144-40-022', 'Gt-Line', 'Sportline', 'Nomade', '21144-40-021', 'Trophy', 'Premium', 'Спорт', 'Image', 'Sport&Style', 'Терра', 'Базовая', 'XTR', 'Play', 'Basis', 'Design', 'Laurin&Klement', 'Premier', 'SL3', '2M', 'LTD', 'WELL', 'GTI', 'Siv', 'MT3', 'NR', 'Tg-Fl14C', '360', 'Spike', 'Technology', 'Match', "Luxe'24", 'GLX', "Comfort'22", 'Classic’22', 'ES', 'WaY', 'Essentia', 'Avantgarde', 'Tendance', "[BLACK]'22", 'AT7', 'Elegancе', 'Active+', 'Original', 'Vr14C', 'Individual', 'LTZ2F', 'Allstar', 'Lux', 'Travel', 'Hi-Tech', 'Kombi', 'GLE', 'Tg12C', 'Excalibur', 'SVAUTOBIOGRAPHY', 'Tekna', "Comfort'24+Мультимедиа", 'CUP', 'Луна', 'Comfort', 'In14C', 'LT', 'Family', '2.0R', 'GT', 'SE+', 'МТ3', 'PROSAFETY', 'DX', 'MPS', 'First', 'MT1', 'Vogue', 'Black&Brown', '23490-A7-452', 'Respect', 'SDX', 'LTZM', 'Shogun', 'LE', 'Sensory', 'Trust', 'Nismo', 'Экспедиция', 'GXR', '2.0L', 'Avenue', 'Calligraphy', 'Essential', 'Instinct', "Life'24", 'Life', 'Sportium', 'Classique', 'Instyle', 'Sport', 'Трофи', 'Drive', 'TRD', '21104-82', 'L&K', 'Summum', 'Voyage', 'LS', 'XE', 'LEM', 'Jet', 'Limited', 'Bluef.', 'PanAmericana', "Classic'22", 'Autobiography', 'Performance', 'Multispace', 'MID', 'SV', 'SUV', 'Луна+', 'JLX-A', 'AT8', 'Trek', 'Unlimited', 'EX', 'Elite', 'Intense+', 'Comfort+', 'Supercharged', "Comfort'24", 'Pulse', 'VTR+', 'Competition', 'Tech', 'Ghia', 'CX', 'Trailhawk', 'YOU', 'Silverline', 'Confort', '#Club', 'Sochi', 'Standart', 'Core', 'Star', 'R-Design', "[BLACK]'24", 'Suriken', 'Premuim', 'ST', 'WRX', 'Startline', 'MT2', 'Комфорт', 'GR', 'Extreme', 'Air', 'President', 'Rubicon', 'ELEGANCE', 'Black', 'Impulse', '21061', 'Base', 'Optimum', 'Tg12Lx', "KHL'24", 'Touring', 'S', 'Hi-Tech+', 'Cooper', '21144-30-012', 'BM', 'SV2', 'Entry', 'PE+', 'Люкс', 'Status', 'Trend&Fun', 'D-Sign', 'A', 'Elegant', 'GL-X', 'Trendline', 'Максимум', 'Diva', 'Edition25', 'Comfort+Navi', 'Atacama', 'Inform', 'Progressive', 'Fun', 'HIGH', 'Macan', 'Club', 'Mid', 'AT4', "Enjoy'24", 'Cosmo', '#Club+Multimedia', 'xLine', 'JLX', 'BlueEFFICIENCY', 'Joy', "Luxe'22", 'Urban', '[BLACK]', 'Top', 'MX', 'Premium+SV', 'XNN', 'Prestige+', "#Club'23", 'Optima', 'Scout', 'XS', 'Euro2012', 'Promo', 'CLASSIC', 'Triumph', 'Shiro', 'Feline', 'Km12C', 'SiR', 'DE', 'Edition', 'GL', "#Club'24", 'Luna', 'Grande', 'Би-2', 'XR', 'Laredo', 'Ms14Lx', 'City', 'Dreamline', 'High+', 'STD', 'Bluetec', 'Expedition', 'Titanium', 'Лимитед', 'Hi-tech', '21144-22-010', 'Noblesse', 'Vr14Lx', 'Primary', 'High-Tech', 'Allure', 'Exclusive', 'Expression', 'Active', 'Elbrus', "#CLUB'22", 'LTZ3V', 'Diesel', 'F', 'Элеганс', 'LX', 'YV', 'In14B', 'Standard', 'SX', 'Cup', 'L', 'R-line', 'Techno', 'PE', 'HL3', "Comfort'23", 'JLX-E', 'G', '21065', 'S-Limited', '23490-A7-450', 'CS', 'XSE', 'LE-R', 'Lite', 'AVANTGARDE', 'LTZ', 'Action', 'Best', 'Start', 'Passion', 'R-Line', 'Fresh', "Techno'24", 'Ultimate', 'Inspire', 'Adventure', 'Authentique', 'Modern', '23490-A1-011', 'Ambition', "Classic'24", 'Westminster', 'AT5', 'Connect', 'SE', 'M2', 'GLS', 'Noire', 'TOP', 'Trend', 'Anniversary', 'Supreme+', 'Intense', 'X-Line', 'JX-E', 'Luxe', 'Direct', "Classic'23", 'Enjoy', 'Tg-Fl14Lx', 'TOP+', 'Классик', 'Advanced', 'Lounge', 'Prestige', 'Pro', '21144-20-010', 'Executive+', 'Inscription', 'Basic', 'Experience', 'KHL', 'Collection', 'Outdoor', 'Advance', 'Luxe+', 'Km14C', 'Kasten', 'Tg13C', 'Tg13Lx', 'SL', 'Deluxe', 'Access', 'M', 'X', "Urban'24", 'Trend+', 'SXT', 'Fleet', 'In12C', 'Style', 'Lifestyle', 'Business', 'Tour', 'Plus', 'Стандарт', 'Оптима', 'Sahara', 'Luxury+', 'Welcome', 'XT', 'FIFA', 'Bn12C', 'Авангард', 'Reference', 'SiV', 'Luxury+Four', 'Invite', 'Select', 'Luxury+Navi', 'Momentum', 'SE+Perso', 'Ambiente', 'HSE', 'Portfolio', 'JLX-EL', 'LC', 'Premium+', 'DLX', 'Attraction', 'Oxygo', '3D', 'Dynamique', 'Hightech', 'Royal', 'MT5', 'TRX', 'RS', 'SS', 'CrossCaddy', 'MT', 'BX', 'Classic', 'Utility', 'CDX', 'Elegance', 'Norma', 'NAV', "Quest'22", 'Престиж', 'Сол', 'Executive', 'GLCM', 'VF', 'Offroad', 'Юбилейный', 'Privilege', 'Comfortline', 'Tg-Fl13C', 'Bn14C', 'Platinum', 'Trendy', 'S-Edition', 'S/C', 'Conceptline', 'Quest', 'Flagship+', 'High', 'Track&Field', 'Overland', 'Way', 'GT-Line', 'XLT', 'Murano', 'Столица', 'Elegance+', 'Blueef', 'Comfortable', 'HVX', 'VTR', 'Medalist', 'Supreme', 'Экспедиционный', 'AUTOBIOGRAPHY', 'Origin', 'Highline', 'Energy', 'LE+', '6AB', 'Live', 'MT6', 'Max', 'Velour', 'Xline', 'Elegance+Four', 'GLC'],
'body_type': ['Фургон', 'Седан', 'Пикап', 'Внедорожник', 'Кабриолет', 'Лифтбек', 'Минивэн', 'Универсал', 'Микроавтобус', 'Купе', 'Хетчбэк'],
'drive_type': ['Передний', 'Задний', 'Полный'],
'engine_type': ['Электро', 'Бензин', 'Гибрид', 'Дизель', 'Газ'],
'doors_number': ['2', '3', '4', '5'],
'color': ['Белый', 'Голубой', 'Фиолетовый', 'Пурпурный', 'Коричневый', 'Серебряный', 'Зелёный', 'Серый', 'Жёлтый', 'Золотой', 'Оранжевый', 'Бежевый', 'Бордовый', 'Розовый', 'Чёрный', 'Синий', 'Красный'],
'pts': ['Не указано', 'Дубликат', 'Электронный', 'Оригинал'],
'audiosistema': ['Не указано', '4 колонки', '2 колонки', '6 колонок', '8+ колонок'],
'diski': ['Не указано', '24"', '20"', '30"', '12"', '13"', '17"', '19"', '14"', '27"', '25"', '21"', '15"', '29"', '7"', '23"', '8"', '28"', '18"', '10"', '22"', '11"', '26"', '16"'],
'electropodemniki': ['Не указано', 'Передние и задние', 'Только передние'],
'fary': ['Не указано', 'Светодиодные', 'Галогенные', 'Ксеноновые'],
'salon': ['Не указано', 'Комбинированный', 'Кожа', 'Велюр', 'Ткань'],
'upravlenie_klimatom': ['Не указано', 'Кондиционер', 'Климат-контроль двухзонный', 'Климат-контроль однозонный'],
'usilitel_rul': ['Не указано', 'электро-', 'электрогидро-'],
'steering_wheel': ['Левый', 'Правый'],
'crashes_count': ['0', '1', '2', '3+'],
'owners_count': ['1', '2', '3', '4+']
}
custom_css = """
/* Настройки выпадающих списков */
.dropdown-menu, .select-wrap, ul[role="listbox"] {
background-color: white !important;
opacity: 1 !important;
z-index: 9999 !important;
border: 1px solid #000000 !important;
box-shadow: 0px 10px 20px rgba(0,0,0,0.2) !important;
}
.item, li[role="option"] {
background-color: white !important;
color: black !important;
border-bottom: 1px solid #eee;
}
.item:hover, li[role="option"]:hover {
background-color: #f0f0f0 !important;
color: #000 !important;
}
input[role="combobox"] {
background-color: white !important;
color: black !important;
}
/* Настройки слайдера (Черный стиль) */
:root {
--slider-color: #000000 !important;
}
input[type="range"]::-webkit-slider-thumb {
background-color: black !important;
border-color: black !important;
}
/* Рамка числового ввода у слайдера */
input[type="number"] {
border: 1px solid #000000 !important;
}
"""
# --- Пользовательский трансформер для multi-label (нужен для загрузки препроцессора) ---
class MultiLabelBinarizerTransformer:
def __init__(self):
self.mlb = MultiLabelBinarizer(sparse_output=False)
self.fitted = False
def _clean_input(self, x):
if x is None or (isinstance(x, float) and np.isnan(x)):
return []
if isinstance(x, (list, np.ndarray)):
cleaned = [str(i) for i in x if i is not None and not (isinstance(i, float) and np.isnan(i))]
return cleaned
return [str(x)]
def fit(self, X, y=None):
try:
lists = X.iloc[:, 0].apply(self._clean_input)
self.mlb.fit(lists)
self.fitted = True
except Exception as e:
print(f"Ошибка в MultiLabelBinarizer: {e}. Пропускаем колонку.")
self.fitted = False
return self
def transform(self, X):
if not self.fitted:
return np.zeros((len(X), 1))
lists = X.iloc[:, 0].apply(self._clean_input)
return self.mlb.transform(lists)
def get_feature_names_out(self, input_features=None):
return self.mlb.classes_ if self.fitted else ['dummy']
def predict(*args):
yield "Идёт рассчёт стоимости..."
keys = [
"body_type", "drive_type", "engine_type", "color",
"pts", "audiosistema", "diski", "fary", "salon",
"upravlenie_klimatom", "steering_wheel", "crashes_count", "owners_count",
"production_year", "mileage", "doors_number", "usilitel_rul", "electropodemniki"
]#"equipment",
mult_to_none = ["equipment", 'protivoygonnaya_sistema_mult', 'multimedia_navigacia_mult', 'audiosistema_mult',
'pomosh_pri_vozhdenii_mult', 'salon_mult', 'obogrev_mult', 'aktivnaya_bezopasnost_mult',
'upravlenie_klimatom_mult', 'pamyat_nastroek_mult',
'shini_i_diski_mult', 'fary_mult', 'electroprivod_mult', 'podushki_bezopasnosti_mult']
# Создаем словарь: если значение "Не указано", заменяем на None
inputs = {
key: (val if val != "Не указано" else None)
for key, val in zip(keys+mult_to_none, list(args)+[None]*len(mult_to_none))
}
new_df = pd.DataFrame([inputs])
# --- Функция расстояния до Москвы ---
def distance_to_moscow(lat=58.59, lon=49.66):
moscow_coords = (55.7558, 37.6173)
if pd.isna(lat) or pd.isna(lon):
return np.nan
return geodesic((lat, lon), moscow_coords).km
# --- Очистка числовых колонок ---
def clean_numeric_cols(df, cols_to_clean):
for col in cols_to_clean:
if col in df.columns:
df[col] = df[col].astype(str).replace({'3+': '3', '2+': '2', 'nan': np.nan})
df[col] = pd.to_numeric(df[col], errors='coerce')
return df
# --- Дополнительный feature engineering ---
def advanced_feature_engineering(df):
# Преобразуем owners_count в числовой тип
if 'owners_count' in df.columns:
df['owners_count'] = pd.to_numeric(df['owners_count'], errors='coerce').fillna(0).astype(int)
# Преобразуем mileage в числовой тип
if 'mileage' in df.columns:
df['mileage'] = pd.to_numeric(df['mileage'], errors='coerce').fillna(0)
# Возраст авто
current_year = 2026
if 'production_year' in df.columns:
df['age'] = current_year - df['production_year']
df = df.drop(columns=['production_year'])
# Обработка close_date
if 'close_date' in df.columns:
df['close_date'] = pd.to_datetime(df['close_date'], errors='coerce')
df['close_year'] = df['close_date'].dt.year.fillna(current_year)
df['close_month'] = df['close_date'].dt.month.fillna(1)
df = df.drop(columns=['close_date'])
# mileage_per_owner
if 'mileage' in df.columns and 'owners_count' in df.columns:
df['mileage_per_owner'] = np.where(df['owners_count'] > 0, df['mileage'] / df['owners_count'], 0)
return df
# --- Загрузка модели и препроцессора ---
preprocessor = joblib.load('car_price_preprocessor.pkl')
model = joblib.load('stacking_car_price_model.pkl')
# --- Применение feature engineering ---
new_df = advanced_feature_engineering(new_df)
# --- Добавление пространственных признаков ---
new_df['dist_to_moscow'] = new_df.apply(lambda row: distance_to_moscow(), axis=1)
if 'mileage' in new_df.columns:
new_df['mileage_log'] = np.log1p(new_df['mileage'])
new_df = new_df.drop(columns=['mileage'])
# --- Очистка числовых колонок ---
cols_to_clean = ['owners_count', 'crashes_count']
new_df = clean_numeric_cols(new_df, cols_to_clean)
# --- Биннинг редких категорий (если применимо) ---
single_cat_cols = [col for col in [
'body_type', 'drive_type', 'engine_type', 'doors_number', 'color', 'pts', 'steering_wheel',
'audiosistema', 'diski', 'electropodemniki', 'fary', 'salon', 'upravlenie_klimatom', 'usilitel_rul',
'owners_count', 'crashes_count'
] if col in new_df.columns]
# --- Трансформация данных с помощью препроцессора ---
new_data_processed = preprocessor.transform(new_df)
# --- Предсказания ---
predictions_log = model.predict(new_data_processed)
# --- Обратное преобразование в оригинальную шкалу (цены) ---
predictions = np.expm1(predictions_log)
yield f"{round(float(predictions[0]))} рублей"
with gr.Blocks() as demo:
gr.Markdown("# 🚗 Калькулятор стоимости авто")
with gr.Row():
with gr.Column():
# equipment = gr.Dropdown(
# choices=OPTIONS['equipment'],
# label="⚙️ Комплектация",
# filterable=True,
# allow_custom_value=False,
# value="Не указано"
# )
body_type = gr.Dropdown(choices=OPTIONS['body_type'], label="🚙 Тип кузова")
drive_type = gr.Dropdown(choices=OPTIONS['drive_type'], label="⚙️ Привод")
engine_type = gr.Dropdown(choices=OPTIONS['engine_type'], label="⛽ Двигатель")
production_year = gr.Dropdown(choices=list(range(1960, 2026)), label="⌛ Год производства")
mileage = inputs=gr.Slider(minimum=0, maximum=1000000, value=50000, step=1000, label="🛤️ Пробег")
with gr.Column():
steering_wheel = gr.Dropdown(choices=OPTIONS['steering_wheel'], label="🛞 Руль", value="Левый")
owners_count = gr.Dropdown(choices=OPTIONS['owners_count'], label="👤 Владельцы")
crashes_count = gr.Dropdown(choices=OPTIONS['crashes_count'], label="💥 ДТП")
color = gr.Dropdown(choices=OPTIONS['color'], label="🎨 Цвет")
with gr.Accordion("Дополнительные параметры", open=False):
with gr.Row():
with gr.Column():
pts = gr.Dropdown(choices=OPTIONS['pts'], label="📄 ПТС")
salon = gr.Dropdown(choices=OPTIONS['salon'], label="💺 Салон")
audiosistema = gr.Dropdown(choices=OPTIONS['audiosistema'], label="🎵 Аудио")
doors_number = gr.Dropdown(choices=OPTIONS['doors_number'], label="🚪 Число дверей")
electropodemniki = gr.Dropdown(choices=OPTIONS['electropodemniki'], label="↕️ Подьёмники")
with gr.Column():
fary = gr.Dropdown(choices=OPTIONS['fary'], label="💡 Фары")
upravlenie_klimatom = gr.Dropdown(choices=OPTIONS['upravlenie_klimatom'], label="❄️ Климат")
diski = gr.Dropdown(choices=OPTIONS['diski'], label="💿 Диски")
usilitel_rul = gr.Dropdown(choices=OPTIONS['usilitel_rul'], label="⚙️ Усилитель руля")
btn = gr.Button("Рассчитать", variant="primary")
output = ""
inputs = [
body_type, drive_type, engine_type, color,
pts, audiosistema, diski, fary, salon,
upravlenie_klimatom, steering_wheel, crashes_count, owners_count,
production_year, mileage, doors_number, usilitel_rul, electropodemniki
]
# Центрирование с помощью колонок
with gr.Row():
with gr.Column(scale=1): pass
# Компонент Label для красивого вывода
output = gr.Label(
label="Прогноз стоимости:",
show_label=False, # Скрываем маленькую надпись сверху для чистоты
num_top_classes=0 # Нам не нужна классификация, только одно значение
)
with gr.Column(scale=1): pass
btn.click(
fn=lambda: "Идёт рассчёт стоимости...",
outputs=output
).then(
fn=predict,
inputs=inputs,
outputs=output,
show_progress="full"
)
if __name__ == "__main__":
demo.launch(css=custom_css, ssr_mode=False, share=True) |