<jupyter_start><jupyter_text>Определение региона для бурения новой скважины по прогнозируемой прибыли**Проект №7 Яндекс.Практикум - Data Science**  Описание проекта**Исходные данные:**Пробы нефти в трёх регионах и характеристики для каждой скважины в регионе.**Цель проекта:**Построить модель для определения региона, где добыча принесёт наибольшую прибыль. Использовать при анализе возможной прибыли и рисков технику Bootstrap.**Условия задачи:**При разведке региона исследуют 500 точек, из которых с помощью машинного обучения выбирают 200 лучших для разработки.Бюджет на разработку скважин в регионе — 10 млрд рублей.При нынешних ценах один баррель сырья приносит 450 рублей дохода. Доход с каждой единицы продукта составляет 450 тыс. рублей, поскольку объём указан в тысячах баррелей.После оценки рисков нужно оставить лишь те регионы, в которых вероятность убытков меньше 2.5%. Среди них выбирают регион с наибольшей средней прибылью.  Структура проекта* [1. Загрузка, анализ и подготовка данных](start)* [2. Обучение и проверка модели](model)    * [2.1 Обучение модели и предсказание на валидационной выборке](predict)    * [2.2 Анализ результатов обучения](analysis)* [3. Расчёт прибыли](earnings)    * [3.1 Подготовка к расчёту прибыли](earnings_1)    * [3.2 Расчёт прибыли и рисков](earnings_1)* [4. Общий вывод](conclusion)  1. Загрузка, анализ и подготовка данных  Импортируем необходимые библиотеки<jupyter_code>import itertools
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.utils import shuffle
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import LinearRegression
dataset_names = ['geo_data_0.csv', 'geo_data_1.csv', 'geo_data_2.csv']
data = []

try:
    for dataset in dataset_names:
        data.append(pd.read_csv(f'./datasets/{dataset}', sep=','))
        print(f'Прочитан файл с данными: "./datasets/{dataset}"')
except:
    try:
        for dataset in dataset_names:
            data = pd.read_csv(f'/datasets/{dataset_name}', sep=',') # yandex.praktikum
            print(f'Прочитан файл с данными: "/datasets/{dataset_name}"')
    except Exception as err:
        print(repr(err))
data[0].head(5)
data[1].head(5)
data[2].head(5)<jupyter_output><empty_output><jupyter_text>Проверим есть ли у нас пропуски в данных<jupyter_code>data[0].info()
data[1].info()
data[2].info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000 entries, 0 to 99999
Data columns (total 5 columns):
 #   Column   Non-Null Count   Dtype  
---  ------   --------------   -----  
 0   id       100000 non-null  object 
 1   f0       100000 non-null  float64
 2   f1       100000 non-null  float64
 3   f2       100000 non-null  float64
 4   product  100000 non-null  float64
dtypes: float64(4), object(1)
memory usage: 3.8+ MB<jupyter_text>Описание данных**Признаки*** id — уникальный идентификатор скважины;* f0, f1, f2 — три признака точек (неважно, что они означают, но сами признаки значимы);* product — объём запасов в скважине (тыс. баррелей).  Проверим повторяется ли индентификатор скважины<jupyter_code>data[0]['id'].value_counts()
data[0].loc[data[0]['id'] == '74z30']<jupyter_output><empty_output><jupyter_text>К сожалению, мы не знаем причин появления нескольких строк данных по одним и тем же скважинам и не знаем можем ли мы объединить признаки эти скважин, поэтому избавимся от данных по этим скважинам<jupyter_code>for i in range(len(data)):
    condition = (data[i]['id'].value_counts() > 1) # if 'id' exist more than 1 time
    indexes = data[i]['id'].value_counts()[condition].index
    data[i] = data[i].loc[~data[i]['id'].isin(indexes)]
for i in range(len(data)):
    print(f'Количество строк с данными по {i} региону: {data[i].shape[0]}')<jupyter_output>Количество строк с данными по 0 региону: 99980
Количество строк с данными по 1 региону: 99992
Количество строк с данными по 2 региону: 99992<jupyter_text>Установим id скважины как индекс<jupyter_code>for i in range(len(data)):
    data[i].set_index('id', inplace=True)
    print(data[i].head(3))
    print()<jupyter_output>f0        f1        f2     product
id                                             
txEyH  0.705745 -0.497823  1.221170  105.280062
2acmU  1.334711 -0.340164  4.365080   73.037750
409Wp  1.022732  0.151990  1.419926   85.265647

              f0        f1        f2     product
id                                              
kBEdx -15.001348 -8.276000 -0.005876    3.179103
62mP7  14.272088 -3.475083  0.999183   26.953261
vyE1P   6.263187 -5.948386  5.001160  134.766305

             f0        f1        f2    product
id                                            
fwXo0 -1.146987  0.963328 -0.828965  27.758673
WJtFt  0.262778  0.269839 -2.530187  56.069697
ovLUW  0.194587  0.289035 -5.586433  62.871910<jupyter_text>Рассмотрим распределение значений по каждому из признаков<jupyter_code>data[0].describe()
data[1].describe()
data[2].describe()<jupyter_output><empty_output><jupyter_text>Вывод* Имеем 3 региона с 100000 скважинами. * Пропуски в данных отсутствуют.* Имеются скважины с нулевым запасом.* Во всех регионах почти равно среднеквадратичное отклонение запаса в скважине, но в регионе "1" значительно меньше средние и медианные запасы.* Удалили для использования в обучении модели 20, 8 и 8 строк с данными с повторяющимеся 'id' скважин в "0", "1" и "2" регионах соответственно.  2. Обучение и проверка модели  Разобьём данные на обучающую и валидационную выборки в соотношении 75:25<jupyter_code>features_train = []
features_valid = []
target_train = []
target_valid = []

for i in range(len(data)):
    features = data[i].drop(['product'], axis=1)
    target = data[i].loc[:,'product']

    X_train, X_valid, y_train, y_valid = train_test_split(features, target, test_size=0.25, random_state=91)
    features_train.append(X_train)
    features_valid.append(X_valid)
    target_train.append(y_train)
    target_valid.append(y_valid)<jupyter_output><empty_output><jupyter_text>2.1 Обучение модели и предсказание на валидационной выборке  Обучим модель и сделаем предсказания на валидационной выборке<jupyter_code>models = []
predictions = []
rmse = []

for i in range(len(data)):
    models.append(LinearRegression(copy_X=True, n_jobs=-1))
    models[i].fit(features_train[i], target_train[i])
    predicted_values = models[i].predict(features_valid[i])
    predictions.append(pd.Series(data=predicted_values, index=target_valid[i].index))
    rmse.append(mean_squared_error(target_valid[i], predictions[i], squared=False))
    print(f'"{i}" - регион\n\tСредний запас предсказанного сырья: {predictions[i].mean():.3f}\n\tRMSE: {rmse[i]:.3f}\n')<jupyter_output>"0" - регион
	Средний запас предсказанного сырья: 92.518
	RMSE: 37.649

"1" - регион
	Средний запас предсказанного сырья: 68.716
	RMSE: 0.895

"2" - регион
	Средний запас предсказанного сырья: 94.791
	RMSE: 39.958<jupyter_text>2.2  Анализ результатов обучения В регионе "1" наша модель линейной регрессии идеально предсказывает запас сырья.Посмотрим корреляцию признаков<jupyter_code>data[1].corr()<jupyter_output><empty_output><jupyter_text>Видим, что коэффициент корреляции признака f2 к product равен почти 1.0Построим график отношения признака f2 к product<jupyter_code>fig, ax = plt.subplots(3,1)
fig.set_size_inches(10, 12)

for i in range(len(data)):
    ax[i].scatter(
        target_valid[i],
        features_valid[i].loc[:, 'f2']
        )
    ax[i].plot(models[i].coef_[2]*features_valid[i].loc[:, 'f2'] + models[i].intercept_, features_valid[i].loc[:, 'f2'], color='red', linewidth=2)
    ax[i].grid(True)
    ax[i].set_title(f'Region "{i}"', fontsize=16)
    ax[i].set_xlabel('Product', fontsize=14)
    ax[i].set_ylabel('f2', fontsize=14)

plt.subplots_adjust(hspace=0.4)
plt.show()<jupyter_output><empty_output><jupyter_text>Оказывается в регионе "1" f2 у нас меняется дискретно (0,1,2...) и на каждом значении f2 запас сырья может принять только одно из двух значений. Очень любопытное наблюдение.  Вывод* Линейная модель плохо подходит для предсказания запасов сырья в регионах "0" и "2". Об этом говорит высокое значение RMSE: около 40 при средних запасах около 90.* Линейная модель идеально предсказывает запас сырья в регионе "1". Но мы наблюдаем аномалии в значениях признака f2 и целевого признака product.  3. Расчёт прибыли  3.1 Подготовка к расчёту прибыли**Рассчитаем достаточный объём сырья для безубыточной разработки новой скважины**<jupyter_code>WELL_EXPLORE_COUNT = 500
WELL_ELABORATION_COUNT = 200
BARREL_INCOME = 450000
BUDGET_FULL = 10000000000
BUDGET_PER_WELL = BUDGET_FULL / WELL_ELABORATION_COUNT
MINIMAL_PRODUCT_FOR_EARNINGS = BUDGET_PER_WELL / BARREL_INCOME

print(f'Бюджет на разработку скважин в регионе: {BUDGET_FULL}')
print(f'Количество исследуемых скважин в регионе: {WELL_EXPLORE_COUNT}')
print(f'Количество исследуемых скважин для разработки в регионе: {WELL_ELABORATION_COUNT}')
print(f'Доход с одной тысячи баррелей: {BARREL_INCOME}')
print(f'Минимальный доход со скважины для безубыточности: {BUDGET_PER_WELL:.0f}')
print(f'Минимальный запас сырья в скважине для безубыточности: {MINIMAL_PRODUCT_FOR_EARNINGS:.3f}')<jupyter_output>Бюджет на разработку скважин в регионе: 10000000000
Количество исследуемых скважин в регионе: 500
Количество исследуемых скважин для разработки в регионе: 200
Доход с одной тысячи баррелей: 450000
Минимальный доход со скважины для безубыточности: 50000000
Минимальный запас сырья в скважине для безубыточности: 111.111<jupyter_text>Вывод* При средних запасах сырья 92, 68 и 94 в регионах "0", "1" и "2" соответственно минимальный требуемый запас сырья для безубыточности равняется 111. Можем отметить, что нам потребуется найти скважины с запасом сырья сыльно больше среднего  3.2 Расчёт прибыли и рисков Функция расчёта прибыли<jupyter_code>def wells_earnings(predicted_values=None, selected_count=None, region=None):
    '''Returns revenue of selected wells.'''
    predicted_sorted = predicted_values.sort_values(ascending=False)
    selected_values = target_valid[region][predicted_sorted.index][:selected_count]
    earnings = (selected_values.sum() - MINIMAL_PRODUCT_FOR_EARNINGS*selected_count) * BUDGET_PER_WELL
    return earnings<jupyter_output><empty_output><jupyter_text>Применим технику Bootstrap для получения распределения средней прибыли по регионам<jupyter_code>state = np.random.RandomState(91)
earnings = []
confidence_intervals =[]
for i in range(len(data)):
    values = []
    lower_upper = {}
    for j in range(1000):
        sample = predictions[i].sample(frac=1, replace=True, random_state=state)
        values.append(wells_earnings(sample.iloc[:WELL_EXPLORE_COUNT], WELL_ELABORATION_COUNT, region=i))
    values = pd.Series(values)
    earnings.append(values)
    lower_upper['lower'] = values.quantile(0.025)
    lower_upper['upper'] = values.quantile(0.975)
    confidence_intervals.append(lower_upper)<jupyter_output><empty_output><jupyter_text>Средняя прибыль, доверительный интервал и вероятность убытков в регионе<jupyter_code>RISK_THRESHOLD = 2.5
risk = []

for i in range(len(data)):
    print(f'"{i}" - регион\n\tСредняя прибыль: {earnings[i].mean():.0f}')
    print(f'\tДоверительный интервал: от {confidence_intervals[i]["lower"]:12.0f} до {confidence_intervals[i]["upper"]:12.0f}')
    risk.append((earnings[i] < 0).mean()*100)
    print(f'\tВероятность убытков: {risk[i]}%')
    print()

region = None
best_earnings = 0
for i in range(len(data)):
    if risk[i] < RISK_THRESHOLD and earnings[i].mean() > best_earnings:
        best_earnings = earnings[i].mean()
        region = i

if region:
    print(f'Наибольшая возможная прибыль при риске убытков менее {RISK_THRESHOLD}% в регионе "{region}"')
else:
    print(f'Во всех регионах риск убытков более {RISK_THRESHOLD}%')<jupyter_output>"0" - регион
	Средняя прибыль: 48133654190
	Доверительный интервал: от  -6327703974 до 102979279768
	Вероятность убытков: 5.0%

"1" - регион
	Средняя прибыль: 46979959368
	Доверительный интервал: от   3060505137 до  91685591846
	Вероятность убытков: 1.9%

"2" - регион
	Средняя прибыль: 38997069530
	Доверительный интервал: от -17285313556 до  94376825402
	Вероятность убытков: 9.2%

Наибольшая возможная прибыль при риске убытков менее 2.5% в регионе "1"
{'fraction_non_alphanumeric': 0.10446544980443286, 'fraction_numerical': 0.061033246414602345, 'mean_word_length': 4.689847009735744, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 3, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '591569', 'license': 'Unlicense', 'n_tokens_mistral': 5336, 'n_tokens_neox': 5510, 'n_words': 1121, 'path': 'Oil_wells/oil_prediction.ipynb', 'repo_name': 'brut0/yandex.praktikum_ds_projects', 'size': '12272'}