Skip to content

DoctorSB/OilHack

Repository files navigation

bb-python

ссылки на доп.материалы

Python: основы и применение

Основы статистики

Цель - научить ребят применять Python для анализа и обработки данных, решая практические задачи, которые возникают в работе инженера-нефтяника.

Какие навыки будут получены в ходе прохождения курса?

  • Изучит базовые принципы при написании кода на Python
  • Научится конструировать алгоритмы в парадигме нефтянного инжиниринга
  • Научится экономить время для решения базовых задач геолога, петрофизика и разработчика
  • Научится составлять скрипты-помощники, которые можно будет применять
  • Освоит инструментарий для визуализации исходных и интерпретированных данных
  • Ознакомится с некоторыми методами машинного обучения и попробует применить их на практике в реальными данными

Начинать чему-то учиться - это всегда трудно, долго, потребует от вас усилий для сосредоточения и фокусировки. Впервые с Python я столкнулся в 2018 году, когда выполнял индивидуальный проект слушателя университета Heriot Watt. Имея за плечами нулевой уровень подготовки, в сжатые сроки, мне пришлось самостоятельно осваивать синтаксис , читать статьи на Медиум и Хабре, просматривать видео-туториалы на Ютубе. Сейчас, Python явялется для меня основным инструментом анализа данных, я почти не использую Excel. Этот курс - результат обобщения нашего опыта и нашего пути изучения возможностей Python.

Данный курс посвящен изучению основ языка программирования Python. Для эффективного использования материалов курса студенту не обязательно быть подготовленным в вопросах программирования, однако наличие каких-то познаний в этой области значительно упростит процесс обучения. Если же вы впервые начинаете программировать, то мы рекомендуем после прохождения курса вернуться в самое начало и еще раз пролистать все лекции - возможно некоторые вещи при первом прочтении были вам не совсем понятны, а после завершения курса станут очевидными.

При написании курса предполагалось, что он будет использоваться студентами инженерных специальностей, поэтому мы ожидаем, что читатель будет хорошо знаком с математическими понятиями. В примерах программ на языке Python могут использоваться алгоритмы из комплексной арифметики, линейной алгебры, математического анализа, теории вероятностей и других разделов высшей математики.

Перед началом работы, скачайте себе на компьютер рабочую директорию, в которой лежат вспомогательные материалы курса: файлы для работы, рабочие тетрадки. Используйте их при изучении разделов курса, тестируйте алгоритмы/методы/функции на предоставленных данных или применяйте свои датасеты.

bb-python.rar

Начало работы

  1. Создание environment

    В языке программировании, слово environment ассоциируется с набором переменных окружения, которые определяют, как программа будет работать на определенном компьютере. Environment может включать в себя путь к установленным библиотекам и другим зависимостям, настройки компилятора, системные переменные и т.д.

    Иными словами, это среда выполнения вашего pet-проекта, в которой содержатся все необходимые библиотеки в тех версиях, которые будут реализовывать ваш код без сбоев и ошибок.

    💡 Переключаясь в работе между pet-проектами, лучше создавать отдельные новые среды.

    В зависимости от того, какая версия Python используется и какие библиотеки установлены, environment может оказывать значительное влияние на работу программы.

    💡 **Задача:** Установите на свой компьютер [рабочую станцию](https://www.anaconda.com/products/distribution) **Anaconda Distribution.**
  2. Установка фреймворка VSCode - это текстовый редактор с открытым исходным кодом, разработанный компанией Microsoft. Он позволяет создавать и редактировать код на различных языках программирования, а также предоставляет множество функций и расширений, которые облегчают процесс разработки или JupyterNotebook - это среда разработки, где сразу можно видеть результат выполнения кода и его отдельных фрагментов

    💡 **Задача**: Установите на свой компьютер редактор [VS Code](https://code.visualstudio.com/) или запустите на компьютере Anaconda.Navigator и в нем уставновите JupyterNotebook
  3. Запустите Anaconda.Navigator, перейдите во вкладку Environment, нажмите кнопку Create , введите имя ядра, выберете версию Python для работы.

  4. Правила организации работы: создание рабочей директории

    Личный опыт показывает, что организация порядка в рабочей папки позволит лучше ориентироваться в рабочем проекте. Развитие вашего проекта сопровождается увеличением объема входных и выходных данных, числа рисунков, инструкций и прочего. Структурируйте инпуты/аутпуты сразу - ниже показан пример, как это сделать:

    Untitled

    В корне директории находятся папки со входными и выходными данными (все пути сохранения , обычно, прописывают в папку output).

    💡 **Задача**: Откройте VS Code и подгрузите ядро проекта. Затем создайте рабочие папки и рабочую юпитеровскую тетрадку (*example**.ipynb*)**

    Подсказка - ядро подрузить надо здесь

    Untitled

  5. Установка и импорт библиотек

    В рамках этого курса, держите в голове мысль “Задачу, которую я пытаюсь решить сейчас, до меня уже решили!”. Это значит, предполагаемые механизмы аналитических расчетов, способов визуализации, обработки и препроцессинга данных - уже реализованы в “питоновских“ библиотеках. Вам лишь остается решить задачу выбора подходящей библиотки и вызова из нее требуемой функции/метода/класса.

    Например, существует бибиотека NumPy. Вот что про нее говорит ChatGPT:

    NumPy (Numerical Python) - это библиотека языка программирования Python, которая предоставляет полезные инструменты для работы с массивами, матрицами и другими типами данных, используемыми в научных вычислениях. В NumPy представлены функции для выполнения математических операций, линейной алгебры, обработки изображений и многое другое. Она является одной из основных библиотек для научных вычислений в Python.

    Для установки библиотки можно воспользоваться следующим алгоритмом.

    • Откройте тетрадь в VSCode/JupyterNotebook.

    • Установите нужную библиотеку, используя команду pip:

      Untitled

    • Импортируйте библиотеку:

      Untitled

    💡 **Задача**: Почитайте в интернете про бибилотеки *Pandas, SkLern, plotly, seaborn,* *matplotlib* - осознайте, для чего они нужны и ознакомьтесь с их возможностями. Установите их внутри вашей *environment* и ипортируйте в рабочую тетрадь

    Запросы в интернете - пока пропустить

Переменные (Основы Python)

  1. Типы: integer, float, string, bool, complex

    Untitled

    $int$ (целое число) - это тип данных, который представляет целые числа без запятых и дробей. Например, $5,$ $-10$, $1000$ и т.д. Они используются для записи количественных значений в программном коде.

    $float$ (число с плавающей запятой) - это тип данных, который представляет вещественные числа. Они содержат запятую, что позволяет записывать дробные числа. Например, $3.14$, $-2.5$, $1000.0$ и т.д. Эти числа используются для более точных значений в программном коде.

    $str$ (строка) - это тип данных для текстовых значений, которые содержат символы, цифры или пробелы. Они формируются путем заключения текстовых значений в кавычки. Например, $"hello world"$, $"123"$, $"python"$ и т.д. Строки используются для работы с текстом в программном коде.

    $bool$ (булево значение) - это тип данных, который представляет логические значение $True$ или $False$. Они используются для выполнения проверок в программном коде. Например, $True$ or $False$, $1==1$ и т.д.

    complex (комплексное число) - это тип данных, который представляет комплексные числа. Они состоят из двух частей - действительной и мнимой. Они записываются в виде a + bi, где a и b - это числа. Эти числа используются для математических вычислений в программном коде. Например, $(3+2j)$, $(-2+4j)$ и т.д.

  2. Способы хранения данных

    $dict, list, array$ — это базовые типы данных в Python, которые часто используются для хранения и обработки структурированных данных.

    • $dict$ — это словарь, который содержит список слов или фраз. Словарь может содержать любое количество слов или фраз, а также дополнительные поля, такие как перевод на другой язык или дата добавления в словарь.
    • $list$ — это список, который содержит элементы одного типа. Список может содержать только один элемент, но может быть бесконечным. Элементы списка могут быть любого типа, включая числа, строки, булевы значения и т.д.
    • $array$ — это массив, который содержит элементы одного типа. Массив может содержать только один элемент, но может быть бесконечным. Элементы массива могут быть любого типа, включая числа, строки, булевы значения и т.д.

    Важно помнить, что типы данных в Python могут быть изменены с помощью методов класса $type()$. Например, метод $type()$ для словаря возвращает тип данных, соответствующий его содержимому.

Некторорые правила синтаксиса в Python:

  1. Программы на Python выполняются последовательно, то есть каждая строка кода выполняется в том порядке, в котором она написана.

  2. Python чувствителен к регистру символов, поэтому my_variable и My_Variable будут считаться разными переменными.

  3. Комментарии в Python начинаются со знака решетки (#) и продолжаются до конца строки. Комментарии игнорируются интерпретатором Python и используются для пояснения кода. Например:

    Untitled

  4. Операторы (сложение $+$, вычитание $-$ , умножение $•$ возведение в степень***,*** деление $/)$ в Python используются для выполнения операций над переменными и объектами, например, арифметические операторы для выполнения математических операций.

  5. Условные операторы ($if, elif, else$) в Python используются для выполнения логических действий в зависимости от того, выполняется ли определенное условие или нет.

  6. Циклы ($for, while$) в Python используются для повторного выполнения определенных операций до тех пор, пока выполняется определенное условие.

  7. Функции $def (x)$ в Python используются для группировки определенного блока кода, который может быть вызван из другой части программы. Функции могут принимать аргументы и возвращать значения.

Таблица базовых типов переменных Python

Тип переменной Описание Изменяемость
$int $ (целочисленный) Хранит целые числа. Целочисленные значения поддерживают арифметические операции: сложение, вычитание, умножение, деление, целочисленное деление и остаток от деления. Неизменяемый
$float $ (с плавающей точкой) Хранит числа с плавающей точкой. В Python существует два типа чисел с плавающей точкой: float и double. В Python 3.x float использует 64 бита и имеет точность до 15 знаков после запятой. В Python 2.x тип float использует 32 бита. Числа с плавающей точкой поддерживают арифметические операции. Неизменяемый
$str $ (строковый) Хранит текстовые данные. Строки в Python могут быть записаны в кавычках или апострофах. Строки являются неизменяемыми, т.е. после создания строку нельзя изменить. Строки поддерживают операции конкатенации, повторения, индексации и извлечения срезов. Также в Python существует множество методов работы со строками. Неизменяемый
$bool $ (логический) Хранит значение True или False. Логические значения часто используются в условных операторах и циклах. Неизменяемый
$list $ (список) Хранит упорядоченную коллекцию элементов. Списки могут содержать элементы разных типов данных. Списки являются изменяемыми, т.е. элементы списка можно добавлять, удалять и изменять. Списки поддерживают операции индексации и извлечения срезов. Также в Python существует множество методов работы со списками. Изменяемый
$dict $ (словарь) Представляют неупорядоченные коллекции пар "ключ-значение". Ключи должны быть уникальными и неизменяемыми, значения могут быть любого типа. Могут содержать элементы разных типов. Могут изменяться, то есть можно добавлять, изменять и удалять элементы. Изменяемый
$tuple $ (кортеж) Представляют упорядоченные коллекции элементов, которые могут быть любого типа. Могут содержать элементы разных типов. Не могут изменяться, то есть после создания кортежа нельзя добавлять, изменять или удалять элементы. Неизменяемый
$set $ (множество) Представляют неупорядоченные коллекции уникальных элементов. Могут содержать элементы разных типов. Могут изменяться, то есть можно добавлять, изменять и удалять элементы. Можно выполнять операции объединения, пересечения и разности множеств. Изменяемый

Операторы Python

Оператор Описание Пример
$=$ Оператор присваивания значения $x = 5$
$+$ Оператор сложения $3 + 2$ вернет $5$
- Оператор вычитания $3 - 2$ вернет $1$
Оператор умножения $3 * 2$ вернет $6$
$/$ Оператор деления $3 / 2$ вернет $1.5$
$//$ Оператор целочисленного деления $3 // 2$ вернет $1$
% Оператор остатка от деления 3 % 2 вернет $1$
$**$ Оператор возведения в степень $3 ** 2$ вернет $ 9$
$==$ Оператор сравнения на равенство $3 == 2$ вернет $False$
$!=$ Оператор сравнения на неравенство $3 != 2$ вернет $True$
$<$ Оператор сравнения на меньше $3 < 2$ вернет $False$
$>$ Оператор сравнения на больше $3 > 2$ вернет $True$
$<=$ Оператор сравнения на меньше или равно $3 <= 2$ вернет $False$
$>=$ Оператор сравнения на больше или равно $3 >= 2 $вернет $True$
$+=$ Оператор добавления значения $x += 5$
$-=$ Оператор вычитания значения $x -= 5$
$and$ Логическое "И" a and b
$or$ Логическое "ИЛИ" a or b
$not$ Логическое "НЕ" not a
$is$ Проверка на идентичность a is b
$is not$ Проверка на неидентичность a is not b
$in$ Проверка на вхождение a in b
$not in$ Проверка на невхождение a not in b

Pandas/Numpy как база работы с файлами + Notebook

Чтение данных

Большая часть информации, с которой работают инженеры, хранится в табличном виде. Традиционно, формат файлов -это excel, csv, txt. Библиотеки Pandas и Numpy - это базовые инструменты-интерфейсы для работы с файлами.

💡 **Совет** “Уделяйте время чтению документации этих библиотек, так как их функционал достаточно широкий”.

Чтобы начать работу, сначала импортируйте библиотеку внтри рабочей тетрадки и выполните процедуру чтения файла, через вызов соответствующей функции бибилотеки Pandas.

Для понимания: вызов методов в библиотеки происходит через точку, т.е pd.название_метода(). Внутри круглых скобок указывают переменные, с которыми будет работать метод или в зависимости от назначения метода, в круглых скобках можно настроить параметры. Если вы работаете в VSCode, то наведите мышкой на название метода и появится подсказка о том, как метод работает, что принимает на вход и что возвращает после его применения. Например (см.рисунок ниже). В случае JupyterNotebook подсказка будет всплывать, если поставить курсор в круглых скобках и нажать Shift+Tab

Untitled

По дефолту, метод воспринимает переданные в него переменные в порядке, как указанно в документации, т.е. в данном случае, первая переменная будет восприниматься как путь к файлу, вторая - как номер/имя листа в excel, с которым будет выполняться работа. Более детально структура методов(функций) будет разобрана в следующих главах Курса.

Итак, давайте прочитаем файл, содержащий таблицу параметров для набора моделей.

import pandas as pd
import numpy as np

path = 'input/' # указываю путь к папке, в которой хранятся входные данные моего проекта
file = 'id-features.xlsx' # указываю имя файла с раширением

df = pd.read_excel(path+file) # Создаю переменную df, в которую записываю читаемый файл
															# библиотека pd содержит метод read_excel, который 
															# выполняет процедуру чтения файла по указанному пути.

df.head() # метод служит для визуализации "головы" данных. Под головой понимается
					# первые пять строк таблицы
💡 **Задача:** Создайте переменную *df_short*, которая будет содержать второй лист (*short_table*) файла if_features.xlsx. Заголовки столбцов содержатся в 1-ой строке.

Документы формата txt могут также быть прочитанными с помощью библиотеки pandas. Для этого следует использовать метод pd.read_table(). В txt-файлах необходимо указывать параметр sep (сепаратор) для того, чтобы данные группировались по столбцам. Сепаратор может быть в виде запятой (sep = ‘,’), пробела ( sep = ‘ ’), точки (sep = ‘.’), табуляции (sep = '\t’) и др.

💡 **Задача**: Создайте переменную *df_txt*, которая будет содержать данных файла XYZ.txt без заголовков столбцов

Поскольку удача не всегда будет на вашей стороне и структура данных может оказаться сложной, лучше знать альтернативный способ чтения файлов формата txt. В этой варианте мы используем функции, вшитые в сам Python.

df_txt = open(path + 'XYZ.txt').read().split(sep='\n')
# open - отрывает файл из указанного пути и записывает его кэш
# .read() - считывает из кэша данные, как **одну строку**
# .split(sep ='\n') - разбивает одну строку на набор строк по заданному сепаратору 

В данном способе чтения, df_txt - это не таблица, а list, содержащий внутри себя строки. Такой формат пока что остается не работоспособным (так как со строками не работают математические операторы) и его нужно преобразовывать дальше. Это управжение мы проделаем в следующих главах Курса.

Совет: “Если ваш код применяется ежедневно и входные файлы приходят из разных источников, формулируйте и выставляейте требования к input’у таким образом, чтобы код не ломался”.

Особенности чтения *.txt документов через open() - читаем строки как float() как int()

Вы уже заметили, что при чтении файлов через питоновские функции, мы будем работать со строками. Из рисунка ниже видно, что внутри листа df_txt есть 5 строк, разделенных запятыми. Внутри каждой строки переменная типа str.

Untitled

Однако, очевидно то, что для этих данных более релевантен тип int или float. Для этого можно воспользоваться библиотекой Numpy. Смысл в том, что изначальный лист df_txt преобразовывается в одномерный массив с конвертацией типа “строка - целое” через специальный параметр dtype.

Untitled

Способы обращения к табличным данным:

Если вы работаете с данными через pandas, то методы(функции) pd.iloc, pd.loc, pd.at будут проводниками к нужной строке, столбцу, ячейке данных. Например, перед вами стоит задача увидеть только нулевой столбец, содержащийся в переменной df_txt (см.рис.ниже).

Untitled

Алгоритм действий будет следующий:

  • написать в ячейке имя переменной, содержащей табличные данные

    df_txt

  • вызвать нужный метод, разделив имя переменной и метода точкой

    df_txt*.*iloc

  • указать в квадратных скобках идекс столбца, к которому надо обратиться. Причем, внутри скобок работат правило [строка , столбец]. Это значит, что все, находится левее запятой относится к ктрокам, а правее - ко столбцам.

    df_txt.iloc[: , 0]

Двоеточие слева от запятой означает запрос: “Я хочу обратиться ко значениям во всех строках нулевого столбца”. Если запись будет иметь вид df_txt.iloc[1:10 , 0], то это означает запрос: “Я хочу обратиться к строкам с 1 по 9 (последняя строка не включается) в нулевом столбце”. Если запись будет иметь вид df_txt.iloc[1:10 , 1:2], то это означает запрос “Я хочу обратиться к строкам с 1 по 9 в столбце №1”.

Если стоит задача обратиться к последнему/предпоследнему/предпредпоследнему столбцу и т.д, то обратная индексация поможет сделать это быстро. Например, два способа обращения к последнему стобцу будут эквиваленты: df_txt.iloc[1:4, -1] и df_txt.iloc[1:4,2]

Untitled

Или два способа обращения к предпоследнему стобцу будут эквиваленты: df_txt.iloc[1:4, -1] и df_txt.iloc[1:4,2]

Untitled

Метод pd.loc работает аналогичным способом, но его вызов немного отличается. Например, ответом на запрос “Обратиться к строкам с 3 по 7 включительно в первом столбце” будет запись df_txt.loc[3:7][1]. Существенным отличием в данном методе является то, обращение к столбцу должно выполяться срого по его названию (как в данных), в то время как метод pd.iloc может выполнить тоже самое обращение через индекс столбца.

Untitled

Методы iloc/loc являются более гибкими, с точки зрения, что они могут показать нужные ячейки в таблице в некотором диапазоне строи и столбцов. Метод pd.at[строка, столбец] требует передавать на вход конкретные ij-координаты. Совет: “В циклах, когда необходимо процедурно заменять старые значения в таблице на новые, лучше применять метод pd.at. Он меньше багует

💡 **Задача**: Для датафрейма id-features.xlsx, посчитайте суммы, которые содержатся в строках с 34 по 49 для столбцов Corey_water, cos_teta и Fault_20. Для расчёта сумм, используейте внутренние методы pandas. Результирующий код должне быть написан в одну строку.

Теперь, когда вы понимаете, как работать с таблицами в pandas’е, нужно изучить основы обращения к данным в массивах Numpy. Лучший способ рассказать об этом, это проанализировать приведенные иллюстрации

Способы обращения к массивам Numpy

Запрос звучит так : “Покажи элементы первого столбца для строк с нулевой по вторую включительно

Запрос звучит так : “Покажи элементы первого столбца для строк с нулевой по вторую включительно

Запрос звучит так : “Покажи элементы с нулевого по первый столбцы включительно внутри строк с первой по третью включительно”

Запрос звучит так : “Покажи элементы с нулевого по первый столбцы включительно внутри строк с первой по третью включительно”

Запрос звучит так : “Начиная с нулевой строки, покажи каждую вторую строку (шаг = 2)”. Знак :: и цифра следующая за ними указывает на шаг, с которым будет происходить фильтрации изначальной таблицы. Поскольку в указанном обращении после цифры y[0::2] отсутствует запятая, фильтрация будет применяться ко всем столбцам сразу.

Запрос звучит так : “Начиная с нулевой строки, покажи каждую вторую строку (шаг = 2)”. Знак :: и цифра следующая за ними указывает на шаг, с которым будет происходить фильтрации изначальной таблицы. Поскольку в указанном обращении после цифры y[0::2] отсутствует запятая, фильтрация будет применяться ко всем столбцам сразу.

Запрос звучит так : Одновременно, для строк и столбцов, покажи все элементы с 1 по 5 строку/столбец включительно, при этом иди с шагом, равным двум”

Запрос звучит так : Одновременно, для строк и столбцов, покажи все элементы с 1 по 5 строку/столбец включительно, при этом иди с шагом, равным двум”

Запрос звучит так: Начиная с первого слоя в 3Д массиве, покажи нулевую строку. Здесь, индексация обращения при фильтрации работает следующим образом [слой, строка, столбец]

Запрос звучит так: Начиная с первого слоя в 3Д массиве, покажи нулевую строку. Здесь, индексация обращения при фильтрации работает следующим образом [слой, строка, столбец]

Запрос звучит так: Начиная с перого слоя в 3Д массиве, покажи в каждом из них строки с нулевой до первой включительно для столбцов, начиная с первого по третий включительно

Запрос звучит так: Начиная с перого слоя в 3Д массиве, покажи в каждом из них строки с нулевой до первой включительно для столбцов, начиная с первого по третий включительно

Приёмы форматирования

В зависимости от решаемой задачи, может потребоваться округление значений до определенного знака после запятой. Внутри Pandas и Numpy встроены свои методы, выполняющие округление данных. Однако, можно использовать внутренную функцию в Python. Например:

  • df['ANI'].round(3) - для датафреймов. Цифра 3 указывает на желаемое количество знаков после запятой
  • x.round(x,2) - для массивов. Здесь x - массив numpy

Приёмы фильтрации через задание условий оказываются очень полезными с точки зрения анализа данных. Для того, чтобы отобразить все строки датафрейма, где в столбце label содержится единица, следующий код:

df[df.label == 1]

Фильтрацию датафрейма можно выполнять используя два или несколько условий. Например, запрос звучит так “Хочу увидеть данные, отфильтрованные по значение в столбце label = 2, при этом столбец Corey_O_W должен содержать значения больше нуля

df[(df['label'] == 1)&(df['Corey_O_W'] > 0)]

Проверить результаты можно, если построить гистограммы распределения:

print(df[(df['label'] == 1) &(df['Corey_O_W'] > 0)]['label'].value_counts())

df[(df['label'] == 1)&(df['Corey_O_W'] > 0)]['Corey_O_W'].hist()

Untitled

Untitled

Задача: Отфильтруйте датафрейм таким образом, чтобы сэмплы(строки) содеражали значение целевой функции (’OF_PresME10_Wq_H’) меньше 2000. В получившемся массиве данных, начиная с 10 и заканчивая 120 строкой включительно с шагом равным 10, пройдитесь с первого до последного столбца с шагом равным 3 и выведите суммы в каждом из них. (Индексация начинается с нуля)

Ответ

Сортировка - полезный метод форматирования датафрейма. “Внутрипитоновская” функция sorted() принимает на вход любой массив данных и сортирует его по возрастанию/убыванию, в зависимости от выставленного параметра reverse

Сортировка может быть одноуровневая, так и многоуровневая. Рассмотрим механизм сортировки в библиотеке pandas.

*data.sort_values(['j', 'i', 'k'], ascending=[True, True, True])*

Многокритериальное агрегирование через pd.groupby()

Что это значит? Это значит, что сначала данные группируются по заданному фильтру, а после для каждой считается какая-нибудь статистическая функция (например, среднее значение или дисперсия).

Например, существует датафрейм df, содержащий результаты процесса адаптация ГДМ в табличном виде, где каждая строка содержит вектор параметров i-ой модели

import pandas as pd
import numpy as np
path = 'input/'
file = 'id-features.xlsx'

df = pd.read_excel(path+file)

Столбец label - содержит экспертные метки, по которым датасет можно разделить на три группы. Давайте вызовем фунцию df.groupby и одновременно применим функцию расчета среднего значения.

df.groupby('label').mean()

Мы увидим, как исходных датафрейм сгруппировал данные по уникальным значениям в столбце label и для каждой группы посчитать среднее значение

Untitled

Однако, анализ данных может строиться на одновременной оценки нескольких статистик: среднего и медианного значений в группе, например. Для этого, в функционале библиотеки pandas предусмотрена функция pd.agg(), куда пользователь , в качестве аргумента функции, должен передать список имен-статистик. В общем виде запись будет выглядить так:

list_of_statistics = ['median','mean']
df.groupby('label').agg(list_of_statistics).round(3)

Другая поставка задачи анализа данных может потребовать от нас сбора статистики для N количества фичей (имена столбцов). Например, “Покажи среднее и медианное значение в трех группах данных по фичам №10 и №17”.

dict_of_statistics ={
    df.columns[10]:['median','mean'],
    df.columns[17]:['median','mean']
    }

df.groupby('label').agg(dict_of_statistics).round(3)

Сначала мы создаем многоуровневый критерий в переменной dict_of_statistics - это словарь, в котором ключи содержат имена фичей из исходного датафрей, а значения - списки с именами вычисляемых статистик .

Задача: Для размеченных экспертных групп (label) в столбцах №14 и №22, отобразите медианное, среднее значения и дисперсию, округлив до 4 знаков после запятой. Затем, построчно, вычислите сумму и отобразите последнее значение

Ответ

Задача: изучете материал по аггрегирующим функциям

Полезные преобразования

Большинство функций, рассмотренных ниже, имеют аналоги в библиотеке numpy. Прочитайте документацию на оффициальном сайте бибилотеки pandas.

pd.concat()

Это встроенная функция библиотеки Pandas, которые объединяет (”конкатит”) несколько датафреймов в один. Синтаксис выглядит следующим образом:

result = pd.concat([df_1,df_2...df_n],axis = 0, ignore_index = False)

Важно: 1) Параметр axis регулирует механизм объединения датафреймов. Если axis = 0 - то датафреймы будут объединяться друг под другом (как строки), а если axis = 1 - то датафреймы будут объединяться “как столбцы”. 2) При данном методе объединения, имена столбцов и индексы строк не влияют на объединение датафреймов, если включен параметр ignore_index = True. При этом, если имена столбцов были категориальными, то они заменятся индексами столбцов. 3) Датафреймы, подлежащие объединению, должны быть сложены в список (квадратные скобки см. пример)

Задача: Для двух файлов data.xlsx и XYZ.txt, создайте один датафрейм, который будет содержать только первые 10 строк из каждого. Объединить датафреймы нужно по столбцам, сохранив исходные имена. Подсказка (сохраните исходные имена датафреймов в списках и объедините спики через команду list_dfs = list_df1.extend(list_df2)

Аналог функции pd.conacat() в библиотеке Numpy - это np.stack().

Задача: почитайте документацию функции “стак” для Numpy

pd.pivot()

Это метод, который преобразует табличные данные в матричный вид. Этот способ преобразования данных является часто встречаемым, когда вы будете работать с выгрузкой из спец.софрта нефтянников (Petrel). Синтаксис выглядит так:

# Пример DataFrame
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]})

# Преобразование DataFrame в матрицу
matrix = df.pivot_table(index='A', columns='B', values='C')

Важно: 1) Метод возращает прямоугольные матрицы (matrix.shape[0]≠ matrix.shape[1]. Если матица не квадратная, то в пустых ячейках будет содержаться NaN

Задача: Прочитайте файл XYZ.txt через библиотеку Pandas и преобразуйте его в матрицу таким образом, чтобы идексами матрицы стал столобец Y (индес столбца 1), а столбцами - столбец X(индес столбца 0). Сами значения заберите из столбца Z (индес столбца 2)

pd.merge()

Метод будет для вас очень полезным, когда потребуется объединить два датафрейма разной формы (df.shape) по стобцу(ам) ключу. Т.е. представьте, что существует две талицы, содержащие различные характеристики объектов. При этом, первая таблица содержит данные по 100 объектам, а вторая по 23. И вам нужно собрать наиболее полный список известных характеристик в одном месте. Эту задачу можно реализовать через синтаксис:

merged_df = pd.merge(merge_df_1, merge_df_2, on = 'name_column_key')

Важно: 1) Порядок датафреймов в методе будет регулировать итоговый вид таблицы. 2) Параметр on отвечает за ключ “мёрджинга” и данный ключ (с учётом синтаксиса) должен содержать как в правом, так и в левом датафрейме. 3) Ключей может быть несколько: чем их больше, тем более узкие границы пересечения между двумя датафреймами 4) При наличии одинаковых столбцов в левом и правом датафреймах, которые не являются ключами

Untitled

Задача: Прочитайте файл merge_sheets.xlsx. Необходимо “смёрждить” первый и второй листы по ключу well

Задача: Разберите статью, чтобы понять как способы компоновки данных через pd.merge()

pd.sort_values()

Метод позволяет выпонить сортировку данных по одному или нескольким ключевым столбцам датафрейма. Синтаксис выглядит следующим образом:

# Пример DataFrame
df = pd.DataFrame({'A': [3, 29, 17], 'B': [4, 5, 6], 'C': [7, 8, 9]})

df.sort_values(by = 'A', ascending = False)
# or
df.sort_values(by=['A'], ascending = False)
# or
df.sort_values('A', ascending = False)

Здесь, все три способа эквиваленты, отличие лишь в способе обращение к столбцу. Параметр ascending = False указывает на то, что сортировка будет происходит от наибольшего значения в столбце A к наименьшему.

Задача: Используя файл merge_sheets.xlsx, выполните сортировку первого листа по столбцу с индексом #3.

Сотрировка может быть применена не только к числовым массивам данных, но также и к строковым. Здесь подбробно на примерах показан механизм, лежащий в основе сортировки строковых переменных

Задач: Посмотреть видоурок

pd.drop()

Выбросить строки/столбцы данных из датафрейма можно через функцию pd.drop(). Синтаксис выглядит следующим образом:

#1
merge_sheet_2.drop(index = 0) # удаляем из датафрейма строку с индексом ноль
#2
merge_sheet_2.drop('well', axis=1) # удаляем из датафрейма столбец well

pd.fillna()

Иногда, таблицы могут содержать пропуски данных, которые при чтении заполняются значением NaN - Not A Number. Причины пропуска данных не так важны в данном случае, но их наличие будет сказываться на работе некоторых алгоритмов. Поэтому эти пропуски можно заполнить какой-нибудь информации, исходя из понимания физических аспектов данных. Заполнить ячейки с пропускамми можно средними значениями, медианными или задать вручную:

df.fillna(val)

Задача: Используя файл nans_df, заполните пропуски данных значением -9999 а также используя среднее значение из столбца №4.

Ответ

Процессы генерации последовательностей

Сгенерировать последовательности можно разными способами, в зависимости от поставленных целей. Для решения этой задачи предлагается использовать, как основу, библиотеки Numpy, Random.

В качестве первого упражнения, давайте сгенерируем последовательный список от 2 до 10:

import numpy as np
seq = np.arange(start,end,step) # Аргументы должны быть целочисленнными
'''
Метод ***np.arange()*** позволяет сгенерировать 
последовательный и упорядоченный список от *start* до *end* (не включительно) c шагом *step*
'''

Часто, в задачах требуется генерация последовательностей, которая описывается некоторым законом распределения. Например, в сложных задачах машинного обучения по распознованию картинок, исследователи накладывают Гауссовский шум на данные (методический приём). Или, на примере генерации куба пористости, которое имеет нормальное распределение, инженеры-нефтянники выполняют вероятностную оценку запасов методом Монте-Карло.

Сгенерировать такую выборку можно так:

import numpy as np
noize = np**.**random**.**normal**(**mu**,** sigma**,** **1000)**

Untitled

Здесь mu и sigma - это параметры нормального распределения (мат.ожидание и дисперсия), а 1000 - количество наблюдений (точек данных) в распределении.

Или, говоря о проницаемости породы, которая характеризуется лог-нормальным распределением, можно сгенерировать выборку таким образом:

np.random.lognormal(mean,sigma,size)

Untitled

Также может возникнуть задача создания равномерного (одинаковый шаг) упорядоченного списка в заданном интервале интереса. Для этой цели можно использовать метод:

import numpy as np
seq = np**.**linspace**(**start**,** end**,** count**)**

Парметры start и end ограничивают пространство генерации набольшим и наименьшим значениями (нижняя и верхняя граница), а count регулирует количество точек в выборке.

Задача: Прочитайте статью в Википедии, а также документацию библиотеки Numpy. Составьте и заполните таблицу по примеру ниже (не более 6 примеров). Если ваше решение выходит за рамки использования одной бибилотеки (и оно работает), то это очень хорошо.

Тип распределения случайной величины Код для реализации в Numpy (etc)
Нормальное noize = np.random.normal(mu, sigma, 1000)
Хи-квадрат noize = np.random.default_rng().chisquare(2,4)

Написание функций

Функция — это кусочек программы, который делает что-то полезное. Например, когда мы пишем программу для расчета суммы и возведения ее в куб, она будет содержать функцию, которая эти реализует задуманную формулу. А еще в ней будут переменные, которые можно менять!

def calclulate_(a, b):
	result = (a + b)**3
	return result
#or

def calclulate_(a, b):
	return (a + b)**3

В данном примере, обе записи эквиваленты: с ростом вашего опыта работы с Python, вы будете стараться писать код кратко, емко, исключая инициации промежуточных переменных/вычислений/сохранений.

Функция в языке программирования - это блок кода, который может принимать определенные входные данные (аргументы), обрабатывать их и возвращать результат в виде выходных данных. Функция может быть вызвана в любой части программы, где это необходимо, и может быть использована для избежания дублирования кода, упрощения структуры программы и улучшения ее читаемости. Т.е. в рутинных вычслениях, вы один раз напишите “много кода” и завернете его в функцию. А когда алгоритм потребует повторых вычислений, то просто обращаетесь к функции.

find files with X in name

Для создания функции в Python необходимо использовать ключевое слово def, после которого следует имя функции и круглые скобки с параметрами функции. Затем следует двоеточие и тело функции, которое начинается с отступа.

def ИМЯ_ФУНКЦИИ(АРГУМЕНТ_1, АРГУМЕНТ_2...АРГУМЕНТ_N):
	"""
	Здесь можно словами описать что возвращает (return) функция,
	а также что из себя представляют аргументы функции.
	"""
	(нажать Tab) ТЕЛО_ФУНКЦИИ
	(нажать Tab) ТЕЛО_ФУНКЦИИ
	(нажать Tab) ТЕЛО_ФУНКЦИИ
	(нажать Tab) ТЕЛО_ФУНКЦИИ
	(нажать Tab) return ЧТО_ТО 

Давайте напишем функцию, которая бы в упрощенной форме оценивала количество запасов на месторождении в одну ячейку:

	def STOIIP(NTG,GRV,phi,So,B):
		'''
		return STOIIP-value 
			NTG(type: float) - net to gross reservoir
			GRV(type: float) - gross rock volume
			phi(type: float) - porosity
			So(type: float) - oil saturation
			B(type: float) - formation volume factor

		'''
		oil_inside = (NTG*GRV*phi*So*B)/B
		return round(oil_inside,4)	

Интерпретация функции: В качестве аргументов, в функцию должны быть переданы 5 параметров из формулы оценки запасов, каждая из которых, по типу, является переменной со знаком после запятой (дробной). Тип данных принято прописывать в документации функции в явном виде. Внутри тела функции инициируется переменная oil_inside, которая выполняет нужный расчет. Функция возвращает значение внутри переменной oil_inside, причем округляет результат до 4-го знака после запятой.

Задача: Напишите функцию, которая принимает на вход коэффициенты a,b,c и возвращает корни квадратного уравнения. В теле функции используйте логическое условие, если дискриминант меньше нуля (**if True: return np.inf**). Опишите документацию.

При написании функции следует учитывать следующие правила:

  • Имя функции должно быть осмысленным и описательным, а также соответствовать стилю именования в Python (обычно используется snake_case: например, функцию для вычисления площади окружности можно назвать “S_area”/”Area”/”get_area_circle”. Когда ваш проект будет прогрессировать и обрастать скриптами, однозначно интерпертируемое название функций позволит вам ускорить мыслительную деятельность.

  • Если функция имеет параметры, то их имена также должны быть описательными и соответствовать стилю именования в Python. Например:

    def get_area_circle (radius):
    	"""
    	return area of circle by radius
    		
    	"""
    	return np.pi*radius**2

    Важно сказать, что если при написании функций используются общеизвестные константы, то не стоит из объявлять при каждом запуске функции, лучше задать их как глобальные или импортировать из библиотек, как в случае числа пи.

  • Функция должна выполнять только одну задачу, и ее длина не должна превышать 20-30 строк. Это эмпирическое правило, не обязательное к исполнению: в первое время, пока у вас будет ставиться практика написания кода, следует дробить код на исполнительные блоки, так как такой подход позволит отслеживать баги/ошибки.

  • Функция должна иметь комментарий, поясняющий ее назначение и входные параметры, если они есть. Документация важна! Даже 5-10 строчек кода, который будет адаптирован под вашу конкретную иследовательскую задачу, нужно писать документацию (хотябы описать return)

  • Функция должна возвращать результат с помощью ключевого слова return. Если внутри функции не будет команды return,то выполнение любых операций в теле функции невозможно будет присвоить какой-либо переменной (см. картинку ниже).

    Untitled

  • Чтобы вызвать функцию, необходимо указать её имя, а затем передать значения входных параметров, если они были определены. Значение, возвращаемое функцией, можно сохранить в переменной или использовать непосредственно.

  • Переменные, созданные внутри функции, являются локальными и доступны только внутри функции. Они не могут быть использованы вне функции, если только не были объявлены как глобальные. Это значит, что если какие-то константы/коэффициенты/множители указываются в теле функции, алгоритм может узнать о их существование только тогда, когда будет вызвана нужная функция. Вообще, примите за правило, что все часто используемые переменные выносить в отдельную ячейку рабочей тетради, сразу под ячейком с импортом библиотек.

  • При вызове функции можно передать значения аргументов в двух форматах: по позиции и по имени. При передаче по позиции аргументы должны быть указаны в том же порядке, в котором они были определены в функции. При передаче по имени аргументы могут быть переданы в любом порядке, указав их имена. Например, в функции ниже, которая вычисляет длину гипотенузы по теореме Пифагора, порядок объявления переменных (катетов) не является важным: значения аргументов можно передавать как по позиции, так и по имени переменной. Более того, исходя из самой формулы, их можно передавать как угодно, не привязываясь к позиции/имени.

    import math
    def pythagorean_theorem(a,b):
    	"""
    	return hypotenuse 
            a(type: float) - leg #2
            b(type: float) - leg #1
    		
    	"""
    	return math.sqrt(a**2+b**2)
    
    pythagorean_theorem(10,20)
    #or
    pythagorean_theorem(a = 10,b = 20)
    #or
    pythagorean_theorem(b = 20,a = 10)

    Однако, может возникать обратная ситуация, когда при вычислениях требуется учитывать порядок аргументов через их позицию или имя. Например, функция ниже возвращает тангенс угла. Изменив порядок переданных агрументов без привязки к их имени, будет получаться различный результат. Т.е tg_corner(10,20) ≠ tg_corner(20,10)

    import math
    def tg_corner(a,b):
    
        """
    	return tangent of x 
            a(type: float) - adjacent cathet
            b(type: float) - opposite cathet
    		
    	"""
        return math.tan(a/b)
    print(tg_corner(10,20))
    >>0.5463024898437905
    print(tg_corner(20,10))
    >>-2.185039863261519

pd.apply()

В этом примере подробно рассказывается о функции apply

map()

Функция map()- это метод, который позволяет создать новый список данных, который будет содержать результаты маппинга к каждому элементу в исходном списке. Функция map() может использоваться для преобразования одного типа данных в другой, изменения порядка значений или выполнения других преобразований над данными. Например, в датафрейме id-features.xlsx существует столбец, содержащий экспекртную разметку данных id-features.iloc[:,-1] и перед вами поставленная задача создать столбец наименование цвета.

Чтобы выполнить процедуру маппинга быстро, можно воспользоваться следующим скриптом:

import pandas as pd

path = 'input/'
file = 'id-features.xlsx'

df = pd.read_excel(path+file)

df["color"] = df.iloc[:,-1].map( {0:'red',1:'green',2:'blue'} )

Интерпретация: Для дафрейма df создается столбец colors, который формируется путем применения функции map() к последнему столбцу в датафрейме df. Функция map() применяется через использование словаря, где ключи - это уникальные значения из последнего столбца датафрейма df, а значения - это соотвествующая новая категория. Иными словами, напротив каждой строки в исходном датафреме, где в последнем столбеце содержится значение == 0, будет задано строковое значение ‘red’ и т.д

Циклы + Notebook

Циклы в Python — это конструкции, которые позволяют выполнять действия над переменной несколько раз. Например, реализовать цикл for, который перебирает список чисел от 1 до 10 и выводит (принтует) каждое, можно следующим образом:

for i in range(11):
	print(i)
>>0
>>1
>>2
>>3
>>4
>>5
>>6
>>7
>>8
>>9
>>10

В указанном выше примере, переменная i будет являться переменной-счётчиком, кодовое слово in указывает на диапазон перебора iв пространстве range(start,end,step). Обычно, исчисление в Python начинается с нуля. Однако, если того требуют условия задачи, начинать, заканчивать и шагать по циклу по разному. Например:

for i in range(3,11,3):
	print(i)
>>3
>>6
>>9

Как и случае написания фукций, тело одного цикла находится на расстоянии одного отступа (табуляции). Если внутри цикла содержится другой цикл, то тело второго цикла должно содержаться на расстоянии двух оступов. Например:

for i in range(3,11,3):
    for j in range(5,11,2):
        print('i =',i,'j =',j)
>>i = 3 j = 5
>>i = 3 j = 7
>>i = 3 j = 9
>>i = 6 j = 5
>>i = 6 j = 7
>>i = 6 j = 9
>>i = 9 j = 5
>>i = 9 j = 7
>>i = 9 j = 9

Как видно из примера, второй цикл характеризуется своей уникальной переменной-счётчиком, которая итерируется в своем диапазоне (от 5 до 10 с шагом 2). Поскольку j-ый цикл является вложенным в i-ый цикл, каждая i-ая итерация сопровождается тремя j-ми итерациями.

Давайте пробежимся циклом по некторому списку двумя способами:

fruits = ['apple', 'banana', 'orange']

for i in range(len(fruits)): #№1
    print(fruits[i])
>>apple
>>banana
>>orange

for i in fruits: #№2
    print(i)
>>apple
>>banana
>>orange

Так как мы хотим принтовать имя каждого фрукта (3 шт.), а метод len() возвращает длину указанного списка/массива/датафрейма, то в первом примере цикл forбудет бежать в диапазоне длины списка fruits(метод range требует указывать целое число итераций). Функция print()выводит каждый i-ый элемент списка fruitsс помощью конструкции : СПИСОК[ИНДЕКС_ЭЛЕМЕНТА_В_СПИСКЕ] - важно запомнить, что ссылаться на i-ый элемент списка нужно через квадратные скобки и указания в них индекса элемента.

Второй пример позоволяет выполнять аналогичную процедуру. Однако, если работать с такой конструкцией цикла, то невозможно будет сослаться на i-ый элемент из другого списка.

Циклы можно использовать не только для данных, содержащих исчисляемые переменные, но и для символов в строках. Например:

message = "Hello, world!"
for symbol in message:
    print(symbol )
>>H
>>e
>>l
>>l
>>o
>>,
>> 
>>w
>>o
>>r
>>l
>>d
>>!

Задача: Используя цикл выше, трансформируйте переменную message = "Hello, world!" в список, содержаший строку message = ["Hello, world!"]. Запустите цикл и сделайте вывод о различиях выводимого результата

Внутри цикла можно выполнять логические операции. Давайте сформулирует задачу и напишем под нее цикл: В указанном списке reference, если i-ый элемент равен 3, то необходимо отобразить сам элемент и его индекс в списке. В противном случае (если условие не выполняется), добавить элемент в новый список not_expected_values = []

refernce = [0,3,1,2,3,5,8,np.inf,5,0.1,'wow']
not_expected_values = []

for i in range(len(refernce)): # итерируем от 0 до 11 - длина списка
    if refernce[i] == 3: # Условие "Если i-ый элемент списка reference равен 3, тогда
        print(refernce[i],i) # принтуем i-ый элемент списка reference и индекс i-ого элемента 
    else: # Во всех остальных случаях, когда уловие if не выполняется
        not_expected_values.append(refernce[i]) # добавляем в новый список 
																								# i-ый элемент списка reference
print(not_expected_values)

>>3 1 # Это первый принт, когда выполнилось условие
>>3 4 # Это второй принт с выполненным условием
>>>[0, 1, 2, 5, 8, inf, 5, 0.1, 'wow'] # Это элементы нового списка. 
																			 # Как видно, троек в нем нет

Теперь, когда вы познакомились с базовыми принципами при написании кода с циклами, вам предлагается применить навык на примере датасета id-features.xlsx. Необходимо циклом пройтись по каждой строке датафрейма и найти такие примеры/сэмплы, для которых значение параметра beta_poro больше, чем 0.199. Если условие выполняется, то в списокmodels_by_condition необходимо добавить имя и лейбл образца, содержащиеся в столбцах Item и label соответственно. Решение показано ниже:

import pandas as pd

path = 'input/'
file = 'id-features.xlsx'
df = pd.read_excel(path+file)
df.shape #показаывает размерность датафрейма 
>> (480, 44) # 480 - количество строк, 44 - количество столбцов

models_by_condition = [] # Пустой список, 
												 # куда будут добавляться данные после выполнения условаий
for i in range(df.shape[0]): # Пространство перебора цикла ограничивается числом строк =480
    if df['beta_poro'][i] > 0.199: # условие
        model = df['Item'][i] # Создается новая перменная, куда записывается i-ое значение
				# из столбца Item
        marker_model = df['label'][i]# Создается новая перменная, куда записывается i-ое значение
				# из столбца label

        models_by_condition.append([model,marker_model]) # В списоке сохраняются две переменные.
				# Обратите внимание, что если нужно сохранить больше чем одно значение в одни момент
				# времени (за одну итерацию цикла), то переменные помещаются в квадратные скобки
				# Иначе говоря, формирует список списков
models_by_condition

>>[['Sector_367', 2],
	 ['Sector_380', 2],
	 ['Sector_400', 2],
	 ['Sector_452', 2],
	 ['Sector_461', 2],
	 ['Sector_529', 3],
	 ['Sector_584', 3],
	 ['Sector_650', 3]]

В задачах с циклами может возникать необходимость преждевременной оставновки break, когда выполнится определенное условия. Делается это с целью снижения расчетной стоимости. Например, итерируя по строкам датафрейма id-features.xlsx мы должны найти такую строку(индекс строки), когда значение в столбце Shift_facies_2 поменяет знак два раза, после чего цикл должен быть оставлен. Давайте напишем код под этот запрос:

Существуют иные формы запуска циклических операций, работающие с “несколькими переменными-счётчиками”. Например, на каждой итерации потребуется выводить индексы строки (переменная-счётчик), а таже все значения в строке. В библиотеке Pandas предусмотрена такая возможность, которая реализуется через следующие формы:

import pandas as pd

path = 'input/'
file = 'id-features.xlsx'
df = pd.read_excel(path+file)import pandas as pd

path = 'input/'
file = 'id-features.xlsx'
df = pd.read_excel(path+file)

for i,row in df.iterrows():# цикл бежит по строкам
		# здесь i - индекс строки
		# row - pd.Series, содержащая данные строки с индексом i

#or

for i,col in df.iteritems():
		# здесь i - имя столбца(если есть) или индекс столбца (если имя отсутствует)
		# col - pd.Series, данные в столбце i

Задача: Для датасета id-features.xlsxнаписать вложенный цикл, который на первом уровне бежит по строкам, а на втором уровне - по столбцам. На каждой итерации второго цикла, необходимо выводить (принтовать) перменные-счётчики двух циклов. В первом цикле должно быть прописано условие остановы: если индекс строки равен 2, то выйти из цикла. Во втором цикле также должно быть прописано условие: если индекс имя столбца 'Krw_Sorw’, тогда выйти из цикла. Давайте напишем код:

import pandas as pd
path = 'input/'
file = 'id-features.xlsx'
df = pd.read_excel(path+file)

for i,row in df.iterrows(): # Цикл итерирует по строкам  
    if i == 2:
        break    
    for j,col in df.iteritems(): # Цикл итерирует по стобцам
        print(i,j)
        if j == 'Krw_Sorw':
            break

Существует еще один способ итерировать в цикле, одновремено по нескольким файлам. Этот метод оказывается полезным, когда необходимо выполнять каки-либо действия над двумя, тремя и более одинаковыми списками-/массивами данных. Пример кода показан ниже:

for i,j in zip([25,100,0],['green','red','blue']):
    print(i,j)

>>25 green
>>100 red
>>0 blue

Краткой и изящной оказывается процедура циклического перебора, если ее реализовать в одной строке. Например, необходимо создать новый список, который содержит только уникальные символы из каждого элемента в столбце Item датафрейма id-features.xlsx

import pandas as pd
path = 'input/'
file = 'id-features.xlsx'
df = pd.read_excel(path+file)

unique_characters = [x.split(sep = '_')[1] for x in df.Item.values]
print(unique_characters)
>>['515',
>> '516',
>> '517',
>>	....
>> '654']

Интерпретировать эту запись можно так: в пустой список unique_charactersнужно записать каждый элемент x из массива df.Item.values . Поскольку каждый x является строковой переменной (можно проверить, если сделать print(df.Item)), содержащий постоянную часть Sector и переменную часть в виде цифр, то справедливо применить операцию x.split()- т.е. разделить строку по заданному сепаратору. В данной задаче, символ _ будет играть роль разделителя строки. В результате, функцияx.split() вернет кортеж (список в круглых скобках) x.split(sep = '_') ---> ('Sector','515'), в котором нам будет интересен второй элемент, так как он уникальный. Поэтому, мы добавили [1] в конце (помним, что индексация в Python начинается с нуля).

Обратите внимание, что список unique_characters содержит значения из переменным типа str (на это указывают апострофы). Если вам потребуется, чтобы значения были исчисляемыми (цифрами), сделать это можно добавление одной команды

unique_characters = [int(x.split(sep = '_')[1]) for x in df.Item.values]
#or
unique_characters = [float(x.split(sep = '_')[1]) for x in df.Item.values]

Цикл while в Python позволяет повторять определенный блок кода до тех пор, пока определенное условие истинно.

Синтаксис цикла while выглядит следующим образом:

while condition:
    # блок кода, который будет повторяться до тех пор, пока condition истинно

condition- это логическое выражение, которое определяет, должен ли продолжаться цикл. Блок кода, который находится под условием while, будет повторяться до тех пор, пока условие продолжает оставаться истинным.

Давайте рассмотрим несколько примеров использования цикла while в Python.

Пример 1: Вывод чисел от 1 до 5

count = 1
while count <= 5:
    print(count)
    count += 1

В этом примере мы используем цикл while, чтобы вывести числа от 1 до 5. Мы начинаем с переменной count, установленной на 1, и затем повторяем блок кода, пока **count**не достигнет 5. На каждой итерации мы выводим значение **count**на экран и увеличиваем его на 1.

Пример 2: Поиск числа в списке

numbers = [1, 2, 3, 4, 5]
target = 3
found = False
index = 0

while not found and index < len(numbers):
    if numbers[index] == target:
        found = True
    else:
        index += 1

if found:
    print("Число найдено в позиции", index)
else:
    print("Число не найдено в списке")

В этом примере мы используем цикл while, чтобы найти заданное число в списке numbers. Мы начинаем с переменной found, установленной на False, и переменной index, установленной на 0. Затем мы повторяем блок кода, пока **found**остается ложным и **index**меньше длины списка. На каждой итерации мы проверяем, равно ли значение target текущему элементу списка, и, если да, устанавливаем found на True. Если число было найдено, мы выводим его позицию в списке, иначе мы выводим сообщение о том, что число не найдено.

Средства визуализации + Notebook

Исследователю необходимо визуализировать результаты своих исследований для того, чтобы лучше понимать процесс исследования и убедиться в его правильности. Визуализация позволяет увидеть данные в графическом виде, что облегчает их восприятие и анализ. Также визуализация помогает улучшить качество результатов исследования и сделать их более доступными для широкой аудитории. Например, графики и диаграммы могут использоваться для отображения статистических данных или результатов экспериментов.

Визуализация данных в Python основана на использовании библиотек matplotlib,seaborn. Эти библиотеки содержат множество инструментов для построения различных типов графиков, таких как столбчатые диаграммы, круговые диаграммы, линейные графики и т.д. Кроме того, они предоставляют возможность изменять цвета и размеры графика, а также добавлять подписи и названия к оси X и Y. Особо приятно для визуализации использовать Plotly. Этот фреймворк (Фреймворк — это программное обеспечение, которое предоставляет набор инструментов и методов для разработки приложений или программ. Фреймворки используются для упрощения процесса разработки программного обеспечения и позволяют сократить время и затраты на создание новых приложений.) является мощным инструментом для создания интерактивных графиков и диаграмм. Он широко используется профессиональными разработчиками и учеными для анализа больших объемов данных и визуализации результатов своих исследований. Использование этих библиотек позволяет исследователям получать точные и достоверные данные из своих исследований. Визуализация данных в Python позволяет им видеть данные в графическом виде, что облегчает их восприятие и анализ. Также визуализация данных в Python позволяет улучшить качество результатов исследования и сделать их более доступными для широкой аудитории. Например, графики и диаграммы могут использоваться для отображения статистических данных или результатов экспериментов.

Больше информации о деталях работы со средствами визуализации вы сможете получить в процессе реализации ваших проектов, анализа статей и чтения научно-популярных изданий (https://towardsdatascience.com/, https://habr.com). В данном курсе, вам предлагается ознакомиться с наиболее популярными техниками (субъективный вывод)

Базовые графики

Гистограмма распределения — это графическое представление данных, где ось X представляет количество наблюдений, а ось Y — значения этого количества. Гистограмма обычно используется для представления данных в виде графика, который показывает распределение значений по интервалам или категориям. Гистограмма распределения может быть использована для отображения различных типов данных, таких как числа, проценты или вероятности. Она может быть полезна для анализа данных, связанных с распределением доходов, численностью населения, частотой заболеваний и т.д

На примре датафрейма id-features.xlsx давайте построим гистограммы распределения несколькими способами

import pandas as pd
path = 'input/'
file = 'id-features.xlsx'
df = pd.read_excel(path+file)

df['beta_poro'].hist(bins = 20)

Гистограмма распределения параметра beta_poro

Гистограмма распределения параметра beta_poro

Внутри библиотеки Pandas встроен pd.hist() - экспресс метод оценки характера распределения параметра в данных. Параметр binsрегулирует количество бинов гисторгараммы (интервалов)

Библиотека import seaborn as sns основана на библиотеках Matplotlib и NumPy. Функционал этой библиотеки позволяет одновременно визуализировать распределение параметра beta_poro с учётом некоторой разметки данных (столбец label). Пример для демонстрации ниже

Подсказка: вы уже знаете, что существует документация для функций. Вызвать ее можно нажав Shift+Tab если вы работаете с JupyterNotebook или через наведение курсора на имя метода(в данном случае на слово histplot)

import pandas as pd
import seaborn as sns
path = 'input/'
file = 'id-features.xlsx'
df = pd.read_excel(path+file)

sns.histplot(df, x = df['beta_poro'],hue = 'label')

Untitled

Другой наиболее распространенный тип графикив - это диаграммы рассеяния или scatter plot, графическое изображение данных, где оси X и Y представляют экспериментальные данные и зависимые переменные соответственно. Давайте построим гистограмму рассеяния (скаттер), где по оси X будет параметр beta_poro, а по оси Y - Corey_O_W, при этом цвет точек будем брать в соответствии со значением из столбца cos_teta, а цветовую шкалу зададим plasma

import pandas as pd
path = 'input/'
file = 'id-features.xlsx'
df = pd.read_excel(path+file)

sns.scatterplot(data=df,
								x="beta_poro", #координаты X
								y="Corey_O_W",# координаты Y
								hue='cos_teta',# группирование данные по значениям/цветам из этого столбца
								palette='plasma')# цветовая палитра

Задача: почитайте о цветовых шкалах в документации

Untitled

Линейные графики также широко распространны для визуализаци результатов. Давайте попробуем отсортировать датафрейм по убыванию значений Целевой Функции OF_PresME10_Wq_H визиализировать процесс адаптации модели и по графику ответим на вопрос: “Какаое количество итераций процесса адаптации потребовалось, чтобы достичь уровня невязки (это OF_PresME10_Wq_H) модели в районе 1000”?.

path = 'input/'
file = 'id-features.xlsx'
df = pd.read_excel(path+file)

df = df.sort_values(by = 'OF_PresME10_Wq_H_OPR',ascending= False).reset_index(drop = True)
# reset_index(drop = True) - требуется для того, чтобы обновить индексацию датафрейма
# после сортировки и не сохранять в данных "старые" индексы строк
sns.lineplot(x=df.index,y= df.OF_PresME10_Wq_H_OPR)

Untitled

Задача: Через цикл, постройте графики функций (отсортированные по убыванию отдельно) для столбцов target_name = ['OF_PresME10_Wq_H_OPR', 'OF_PresME10_Wq_H_WPR','OF_PresME10_Wq_H_WIR', 'OF_PresME10_Wq_H_BHP', 'OF_PresME10_Wq_H'], используя библиотеку matplotlib

Ответ

target_name = ['OF_PresME10_Wq_H_OPR', 'OF_PresME10_Wq_H_WPR', 'OF_PresME10_Wq_H_WIR','OF_PresME10_Wq_H_BHP', 'OF_PresME10_Wq_H']

for i in range(len(target_name)):
    df = pd.read_excel(path+file)
    df = df.sort_values(by = target_name[i],ascending= False).reset_index(drop = True)
    plt.plot(df[target_name[i]],label = target_name[i])
    plt.legend()

Untitled

eCDF

Частный случай гистограммы распределения - это диаграмма накопленного распределения. Здесь и здесь приводятся доводы относительно гипотезы того, что применять диаграмму накопленного распределения лучше, чем базовую гистограмму распределения. Поэтому, в данном курсе, мы советуем вам применять этот инструмент для анализа данных.

Далее будет продемонстрирован пример реализации eCDF и обычной гистограммы распределения:

import numpy as np
import matplotlib.pyplot as plt

sample1 = np.random.normal(loc=80, scale=5, size=300) # Создаем случайную выборку №1
sample2 = np.random.normal(loc=40, scale=5, size=700) # Создаем случайную выборку №2
sample = np.hstack((sample1, sample2)) # Создаем объединенную выборку из №1 и №2
sns.histplot(sample1,bins = 10,color='red')
sns.histplot(sample2,bins = 10,color = 'green')
sns.histplot(sample,bins = 10)

Untitled

А теперь, построим для тех же наборов данных диаграммы eCDF:

import statsmodels.api as sm
ecdf = sm.distributions.ECDF(sample) 
ecdf1 = sm.distributions.ECDF(sample1)
ecdf2 = sm.distributions.ECDF(sample2)

sns.lineplot(ecdf.x, ecdf.y)
sns.lineplot(ecdf1.x, ecdf1.y)
sns.lineplot(ecdf2.x, ecdf2.y)

Untitled

Задача: прочитайте материал, указанный тут и поварьируйте параметр sns.histplot(bins = X). Таким образом вы поймете, как внешний вид графика может влиять на его интерпретацию

Ящики с усами

Ящики с усами или бокс-плоты также полезный инструмент, который показывает разброс (неопределенность) данных. Давайте попробуем построить такой ящик с использованием библиотеки Plotly. Синтаксис этой библиотеки отличается от прывычных в matplotlib или seaborn. Однако, он интуитивно понятен и позволяет получать интерактивные графики.

import plotly.graph_objects as go
import numpy as np
import matplotlib.pyplot as plt

sample1 = np.random.normal(loc=80, scale=5, size=300) # Создаем случайную выборку №1
sample2 = np.random.normal(loc=40, scale=5, size=700) #Создаем случайную выборку №2
sample = np.hstack((sample1, sample2)) # Создаем объединенную выборку из №1 и №2

fig = go.Figure() # Создаем пустую фигуру, в которой можно инициировать бокс-плоты.
# Стоит сказать, что построение любой график из библиотеки должно начинаться 
# через создание фигуры
fig.add_trace( # Создаем первый бокс-плот, куда передадим выборку sample1
    go.Box(
        y=sample1,        
        name = 'sample1',  # присвоим имя первому бокс-плоту     
        boxmean='sd')
        )
fig.add_trace(  # Создаем первый бокс-плот, куда передадим выборку sample2
    go.Box(
        y=sample2,        
        name = 'sample2',  # присвоим имя второму бокс-плоту      
        boxmean='sd')
        )

fig.add_trace( # Создаем первый бокс-плот, куда передадим выборку sample
    go.Box(
        y=sample,        
        name = 'sample',# присвоим имя третьему бокс-плоту  
        boxmean='sd')
        )
    
fig.update_layout( # Настройки отображения фигуры 
    #title=feature, # Заголовок фигуры
    width=600, #ширина фигуры в пикселях
    height=700,    #высота фигу в пискелях
    yaxis_title='Вариация параметра') #подпись оси Y
fig.show() # Отображение фигуры

Untitled

Диаграмма показывает достаточно много информации о выборке: квантили, среднее ± дисперсия среднего, медианного значение, выборосы, максимальное и минимальное. Бокс-плот позволяет одновременно по нескольким характеристикам (многомерное сравнение) сопоставляеть выборки данных.

Тепловые карты

Данные могут храниться в матричном виде и использование диаграмм рассеяния не всегда оправдано, если стоит задача визуализации. Подходящзий и быстрый способ - это использование тепловых карт. Давайте попробуем построить тепловую карту для сейсмических атрибутов. В файле XYZ.txt содержатся три колонки данных: X - координата, Y- координрата и значение атрибута Z, в заданной точке прастранства XY. Первое, что требуется сделать - это прочесть файл через Pandas и переформатировать в матричный вид с помощью метода pd.pivot(). Эту задачу вы уже решили в предыдущих разделах (тут). Когда данные подготовленны, используйте соответсвующий метод визуализации

import pandas as pd
import seaborn as sns

maps_A = pd.read_table('input/XYZ.txt', sep =' ',header=None) #Загрузка сейсмического атрибута
maps_A.columns = ['X','Y','Z']# Зададим имена столбцам

heatmap = pd.pivot_table(maps_A, index='Y',columns='X',values='Z')# Переформатируем 
																																	# данные в матричный вид
sns.heatmap(heatmap) # Построим тепловую карту 
plt.gca().invert_yaxis() # Отсортируем значение по оси Y во время визуализации

Задача: Постройте тепловую карту для файла 1_RMS amplitude_amp_cube.txt, причем рабочими должны быть первые три столбца (XYZ). Столбцы с координатами округлите до целого числа, а столбец Z оставьте без изменения.

Ответ:

maps_A = pd.read_table('input/1_RMS amplitude_amp_cube.txt', sep =' ',header=None).iloc[:,:3]
maps_A.iloc[:,:2] = maps_A.iloc[:,:2].astype(int)
maps_A.columns = ['X','Y','Z']
maps_A
maps_A.head(3)

heatmap = pd.pivot_table(maps_A, index='Y',columns='X',values='Z')
sns.heatmap(heatmap)
plt.gca().invert_yaxis()

Загрузка спецформата данных + Notebooks

Специфика работы нефтянного инженера подразумевает использование специального формата данных, в которых хрянится информация об оскважинных исследованиях (las), о результатах порстроения геологических моделей (кубы свойств), о результатах гидродинамических рассчетов.

Некоторые из них можно прочесть обычным текстовым редактором и осознав структуру, можно создать скрипт Python для чтения/редактирования. В большинстве случаев, уже созданы открытые библиотеки для работы с выгрузками из Petrel, tNav, Techlog и прочее. Однако, в рамка данного курса, мы будем учиться как пользоваться готовыми решениями, так и писать код собственных парсеров.

Парсер — это программа или инструмент, который используется для анализа и обработки текстовых данных. Он может быть использован для извлечения информации из документов, таких как статьи, книги или электронные письма, а также для преобразования этой информации в другой формат, такой как числовые данные или графики. Основная цель парсера заключается в том, чтобы извлечь необходимую информацию из текста и преобразовать ее в удобный для использования вид. Парсеры могут использоваться для анализа текстовых данных различных типов, таких как новости, научные статьи, инструкции по эксплуатации и т.д.

Что говорит о парсере нейронка GigaChat: Создание парсеров определенного формата файлов имеет ряд причин. Вот некоторые из них:

  1. Унификация процесса обработки данных: Парсеры могут использоваться для автоматической обработки большого количества данных одного и того же формата. Это позволяет сократить время и усилия, затрачиваемые на ручную обработку данных.

  2. Обеспечение совместимости: Некоторые форматы файлов могут иметь различные стандарты форматирования и разметки, что может привести к трудностям при работе с ними. Создание парсера соответствующего формата файла позволяет избежать этих проблем.

  3. Оптимизация производительности: Использование парсера позволяет ускорить процесс обработки данных за счет автоматического определения формата файла и соответствующей ему обработки.

  4. Улучшение качества данных: Создание парсера позволяет улучшить качество данных за счет более точной классификации и категоризации.

  5. Возможность интеграции с другими инструментами: Парсеры могут использоваться для интеграции с другими инструментами, такими как библиотеки машинного обучения или инструменты для анализа данных.

acii irap grid

las

RSM

PRT.rar

PRT

PRT (он же “ПРТшник”) - это текстовый документ, содержащий информацию о проведении гидродинамического расчета модели пласта. Обычно, он формируется, если используется симулятор Eclipse. Внутри файла содержится много информации: расчетные значение параметров добычи, давления в скважине, информацию о работе скважин, изменения режима контроля (LRAT/BHP), ошибках при симуляя и прочее. Прочитать его можно используя обычный текстовый редактор (советую использовать в работе этот .

Умение парсить такой файл может значительно ускорить работу нефтянного инженера, когда речь идет о сотнях гидродинамических расчетах. Предлагаю на примере задачи написать парсер ПРТшника.

Задача: Имея набор из PRT-файлов, постройте графики остаточных запасов на каждый шаг (REPORT) всего периода разработки по всему месторождению.

💡 Остаточные запасы на каждую контрольную точку ГДМ содеражатся напротив кодового слова '*CURRENTLY IN PLACE*'. Добыча по всему месторождению содержится в таблице, которая называется *FIELD TOTALS*. Попробуйте разобрать каждую строчку кода и осознать, что именно в ней выполняется. Напишите комментарии к ним
import pandas as pd
import matplotlib.pyplot as plt

from os import listdir
from os.path import isfile, join

path_prt = 'input/prts/'
files = [f for f in listdir(path_prt) if isfile(join(path_prt, f))]

result = pd.DataFrame()
key = ':CURRENTLY IN PLACE       :'

for file in files:        
    x = open(path_prt + file, "r")
    lines = x.readlines()
    values = []
    for line in lines:
        if key in line:
            #print(float(line.split()[4]))            
            values.append(float(line.split()[4]))
    result[file] = values

for file in files:
    plt.plot(result[file].values,label = file)
    plt.legend()

Задача: Попробуйте выполнить предыдущую задачу, но с прицелом на скважину P2

LAS

LAS или ЛАС-файлы это способ хранения данных, полученных в результате геофизических исследований скважин. Внутри себя, лас-файл содержит информацию о скважине (имя), набор кривых ГИС (каротажи) с присущими мнемониками (аббревиатуры/имя каротажа). Существует несколько открытых библотек для чтения каротажных данных, например import lasio .

Чаще всего, запись каротажа начинается задолго до того, как будет пробурен нефтеносный пласт и это значит, что запись прибора может покрывать сотни метров горной породы, из которых целевыми будут лишь десятки. В ЛАСе целевой интервал может определен при помощи вспомогательной информации - таблицы, содержащий отбивки (глубинные отметки) пластов - top и bottom.

💡 Например, каротаж был записан для 100 метров породы относительно дневной поверхности. Дискретность (частота замеров) составляет 1м. Это значит, что сигнал ГИС будет содержать 100 замеров. Известно, что кровля целевого пласта находится на 65 метре, а подошва на 77. Значит, целевой интервал можно выделить следующим образом: `LAS[65:77]`

Давайте сформулируем задачу и напишем код, который позволит ознакомиться с этим форматом данных.

Задача: Требуется подготовить рабочую таблицу, содержащую инофрмация о геофизических скважинных исследованиях для всех скважин из входного набора. Целевой интервал U1-3. Каротажи, которые необходимы - SP, MPZ, GZ3.

💡 Самостоятельно напиши комментарии к каждой строчке кода, чтобы объяснить ее наличие и какую функию она (строчка) выполняет
  • Первый шаг - это загрузка LAS-ов и таблицы с отбивками пластов.

    import pandas as pd
    import numpy as np
    
    from os import listdir
    from os.path import isfile, join
    import lasio
    from tqdm import tqdm
    import plotly.graph_objects as go
    
    las_path = 'input/las/'
    files = [f for f in listdir(las_path) if isfile(join(las_path, f))]
    files
    >>['104P.las',
    >> '190P.las',
    >> '191P.las',
    >> '195P.las',
    >> '196P.las',
    >> '201P.las',
    >> '204P.las',
    >> '205P.las',
    >> '214P.las',
    >> '220P.las',
    >> '224P.las',
    >> '226P.las',
    >> '387P.las',
    >> '472P.las',
    >> '59P.las',
    >> 'Крапивинское_пластопересечения.xls']
  • Разделить список на два: содержащий имена LAS-ов и справочные данные

    las_files = files[:-1]
    tops_files = files[-1]
  • Загрузка файла с отбивками пластов

    tops = pd.read_excel(las_path + tops_files)
    tops.head()

    Untitled

    Здесь, столбец SURFACE содержит имена кровли и подошвы пластов, WELL - имя скважины, X,Y - координаты пластопересечения, MD - глубинная отметка вдоль ствола скважины, TVD - true vertical depth, TVDSS - истинная вертикальная глубина от уровня моря, ALT - kelly bushing или высота роторного стола над уровнем моря. Эта картинка поможет осознать написанное. Для текущей задачи, интересны будут столбцы SURFACE, WELL, MD

  • Таблица с отбивками содержит много неиспользуемой информации, поэтому ее нужно отфильтровать так, чтобы она содержала только интересуемые поверхности и скважины.

    filtered_tops = tops[tops.iloc[:,1].isin([x[:-4] for x in las_files])]
    #фильтурем датафрейм по скважинам
    filtered_tops = filtered_tops[~filtered_tops.iloc[:,0].isin(['Top U1-2','Bottom U1-2'])].reset_index(drop=True)
    #Фильтруем датафрейм по отбивкам пласта
    filtered_tops.head()
  • Поиск в каротаже глубинной отметки, соответствующей кровле или подошве, может не дать результата, если будем искать соответствие через точное сопадение значений. Лучше всего применять способ поиска, основанный на нахождении наименьшей разности между двумя значениями. С этой целью давайте напишем функцию, поскольку операция поиска будет выполняться несколько раз в ходе решения задачи:

    def find_nearest(array, value):
        '''
        Фун-я ищет индекс элемента в массиве array,
        который максимально похож по значению к заданному value
        
        '''
        array = np.asarray(array)
        idx = (np.abs(array - value)).argmin()
        return idx
  • Когда все подготовительные процедуры выполнены, давайте напишем основной скрипт:

    result = pd.DataFrame(columns=['WELL','MD','SP','MPZ','GZ3'])#Пустой шаблон результируеющей таблицы
    
    for i,item in enumerate(set(filtered_tops.iloc[:,1])):#Цикл итерирует по  именам скважин
        
        las = lasio.read(las_path + item + '.las',encoding='windows-1252')#Загрузка las-файла  
       
        top_point = filtered_tops[filtered_tops.iloc[:,1] == item].MD.values[0]
    		# Переменная содержит значение глубины кровли для скважины item
    		# 
        bottom_point = filtered_tops[filtered_tops.iloc[:,1] == item].MD.values[1]  
    		# Переменная содержит значение глубины подошвы для скважины item
        s = find_nearest(las['MD'].astype(float), top_point)
    		# Индекс элемента в las-файле, соответствующий
    		# кровле пласта
        e = find_nearest(las['MD'].astype(float), bottom_point)
    		# Индекс элемента в las-файле, соответствующий
    		# подошве пласта
        result.at[i,'WELL'] = item
        result.at[i,'MD'] = list(las['MD'][s:e])
        result.at[i,'SP'] = list(las['SP'][s:e])
        result.at[i,'MPZ'] = list(las['MPZ'][s:e])
        result.at[i,'GZ3'] = list(las['GZ3'][s:e])

    Результирующая таблица выглядит так:

    Untitled

Задача: Используя библиотеку Plotly, метод add_trace() , в цикле постройте планшет, содержащий все карторажи SP для скважин, из таблицы result. Примерный образ результата должен выглядить так:

Untitled

IRAP ASCII GRID

Файлы формата IRAP ASCII GRID представляют собой наборы данных, организованные в виде иерархической структуры, называемой картой. Каждая карта состоит из набора столбцов, называемых ресурсами, которые представляют собой наборы числовых значений. Каждый ресурс представлен в виде строки, содержащей название ресурса, числовое значение и дополнительные атрибуты, такие как дата и время создания, категория и т.д. Каждая карта содержит несколько слоев данных, называемых уровнями. Уровень включает в себя несколько карт, каждая из которых представляет отдельный набор данных. Каждый уровень имеет свой собственный формат файла и соответствующие методы обработки данных.

Использование файлов форма IRAP ASCII GRID позволяет планировать и анализировать большие объемы данных, обеспечивая удобство работы с данными и возможность визуализации результатов анализа. Кроме того, файлы форма IRAP ASCII GRID могут использоваться для проведения статистических исследований и прогнозирования временных рядов.

По ссылке можно прочитать детальную информацию с примером.

Задача: Прочтите код с комментариями и впишите свои комментарии. Это упражнение позволит вам в будущем лучше понимать работу скрипта. Нарисуйте блок-схему алгоритма

root_path = 'input/grids/' # Указываем путь к файлам с гридами
files_dir = [f for f in listdir(root_path) if isfile(join(root_path, f))]
#В переменную files_dir записываем имена файлов в папке

grids = [] # В этом списке будем хранить промежуточные файлы, неподготовленные

for file in files_dir: #Цикл отрывает гриды и сохраняет "сырые файлы" в списке        
    x = open(root_path + file, "r")
    grids.append(x)  

results = [] # В этом списке будем хранить прочитанные и подготовленные файлы
for grid_file in grids: # Переменная-счётчик grid_file итерирует по списку grids
												# Здесь, grid_file используется в двух ролях - ??каких??
    lines = grid_file.readlines() # Форматируем содержимое "сырого грида" в строки
    for i in range (0, len(lines)):
        lines[i] = lines[i].replace('\n', '')# ??																						
    
    numX = int(lines[0].split(' ')[1]) # Количество строк в файле grid_file 
    numY = int(lines[2].split(' ')[0]) # Количество столбцов в файле grid_file 
    header = lines[:4] # Шапка файла, содержашая мета-данные (см.ссылку выше)
    b_lines = lines[4:] #??? что тут??
    print('x size:', numX)
    print('y size:', numY)
    
    grid_values = np.zeros((numX, numY)) #Создается массив нулей, который 
																				 #будет наполняться информацией из "сырого грида"
    x_i = 0 #Фиксируем начальные координаты(индексы)
    y_i = 0 #Фиксируем начальные координаты(индексы)

    for l in tqdm(b_lines):
        values = l.split(' ')
        for val in values: 
            if int(float(val)) != 9999900: 
							 # 9999900 - Флаг, говорящий о пропуске данных.
							 # Может принимать другие значения, надо проверять в самом файле
										
                grid_values[x_i][y_i] = float(val)

            y_i += 1
            if y_i == numY: #??? зачем проверяется это условие???
                y_i = 0 # обнуление счетчика
                x_i += 1 # "прыгаем на новую строку "сырого грида"
    
    grid_values = np.fliplr(grid_values[::-1,::-1]) # ДЛя

    for i,item in enumerate(header):
        if i == 0:
            dx = int(float((item.split(' ')[2])))
            dy = int(float((item.split(' ')[3])))
        elif i == 1:
            xmin,xmax = int(float(item.split(' ')[0])), int(float(item.split(' ')[1]))
            ymin,ymax = int(float(item.split(' ')[2])),int(float(item.split(' ')[3]))
        else:
            continue
        print(dx,dy,xmin,xmax,ymin,ymax)
    X,Y = np.arange(xmin ,xmax,dx),np.arange(ymin,ymax,dy)
    df = pd.DataFrame(data = grid_values,index=Y,columns=X)

    results.append(df)

for i in results:
    sns.set(rc={'figure.figsize':(7,10),'figure.dpi':80})
    sns.heatmap(i)
    plt.gca().invert_yaxis()
    plt.show()

Untitled

RSM - ИСКЛЮЧИТЬ ИЗ КУРСА ВВИДУ ВЫСОКОЙ СЛОЖНОСТИ ПАРСЕРА

Файл формата .RSM включает в себя следующие данные в незашифрованном виде:

  1. Показатели по м/р FIELD и по скважинам (имя скважин)

  2. Сами показатели: набор ключей типа FOPR, WOPR, WBHP и так далее, они стандартизованы и можно собрать их списком. При парсинге нужно также считать все единицы измерения, они в файле указаны под самими ключами, без них никуда.

  3. Есть временные шаги - месяцы периода разработки, 01.07.2013, 01.08.2013....01.01.2022

  4. Для каждого показателя (ключа) существуют 2 набора данных: исторические (добавляется буква "H" в конец ключа) и модельные (без добавления букв в ключе).

  5. Всего строк в данном файле этого месторождения 85653, их количество зависит от количества скважин и количества временных шагов (годы истории). Ну и от количества ключей, которые выбраны для выгрузки.

  6. Список из наиболее необходимых разработчику ключей для визуализации и аналитики данных приведен в Excel файле.

    Список нужных ключевых слов для RSM.xlsx

import pandas as pd
import numpy as np
import re
from tqdm import tqdm
import plotly.graph_objects as go

rsm_path = 'input/FULL_TNAV__610'
keys = pd.read_excel('input/rsm key.xlsx')

def index_creator(row):
    just,dance = [],[]
    for j,jtem in enumerate(row):
        if jtem != ' ':
            just.append(j)
        else:
            if len(just) > 0:            
                dance.append([just[0],just[-1]+1])
                just= []
    return dance

x = open(rsm_path, "r")
res,flag = [], 0

SPACE  = 4 # Гипепараметр, количество символов для отступа

for i,item in  tqdm(enumerate(x)):    
    item = item.split('\n')[0] 
    indexes = index_creator(item)
    if 'TIME' in item:        
        flag = i + 4  #    
        reference = index_creator(item)        
    if i < flag: # Обработка header
        reference_row = []
        indexes = reference
        for j,jtem in enumerate(indexes):
            
            if '*' in item[jtem[0]:jtem[1]+SPACE]:    
                value  = item[jtem[0]:jtem[1]+SPACE]
                value = 10**float(value.split('*10**')[1])
            else:
                         
                value = item[jtem[0]:jtem[1]+SPACE]              
                value = ''.join(value.split()) 
            reference_row.append(value)

        res.append(reference_row)
    else: # Обработка values
        row = []
        for j,jtem in enumerate(indexes):

            value = item[jtem[0]:jtem[1]]
            value = ''.join(value.split())
            row.append(value)
       
        res.append(row)
    if flag == i:
        flag = 0

res = [x for x in res if x]

def df_maker(header,values):
    it = pd.DataFrame()
    for j,column in header.iteritems():
        check_list = [[j,x,index] for index,x in enumerate(column.values) if type(x) is float] 

        if len(check_list) !=0: 
            head = header.loc[0][j]
            meta = header.loc[1][j]

            if type(header.loc[check_list[0][2]][j]) == float:
                name = 'FIELD'
            elif type(header.loc[check_list[0][2]][j]) == str:
                name = [x for x in header.loc[2:][j].tolist() if x][0]

            values.iloc[:,check_list[0][0]] = values.iloc[:,check_list[0][0]].apply(lambda x: x*check_list[0][1])

            it = pd.concat([it,pd.DataFrame([head,meta,name])],axis = 1,ignore_index=True)
        else:
            head = header.loc[0][j]
            meta = header.loc[1][j]
            try:
                name = [x for x in header.loc[2:][j].tolist() if x][0]
            except:
                if 'TIME' in column.values:
                    name = ''
                else:
                    name = 'FIELD'
            it = pd.concat([it,pd.DataFrame([head,meta,name])],axis = 1,ignore_index=True)      

        df = pd.concat([it,values],axis=0).reset_index(drop = True)
        df.columns = df.loc[0].values
    
    return df

LENGHT = 112 -11

final_df = pd.DataFrame()
for i,row in tqdm(pd.DataFrame(res).iterrows()):

    if 'SUMMARY' in row.values:        
        header = pd.DataFrame(res).loc[i+1:i+4].reset_index(drop= True)        
        values = pd.DataFrame(res).loc[i+5:i+LENGHT].astype(float).reset_index(drop= True)       
        final_df = pd.concat([final_df, df_maker(header,values)],axis=1)              
    else:
        continue

Способы сравнения и анализа данных

Сравнительный анализ используется для анализа больших объемов данных. Он позволяет выявить закономерности и тренды в данных, которые могут быть использованы для прогнозирования будущих событий. Сравнительный анализ выборок данных — это метод статистического анализа, который используется для сравнения двух наборов данных. Он позволяет выявить различия между ними и определить, какой набор данных лучше всего подходит для конкретных целей. Сравнивать можно не только выборки, но временные ряды, например, сигналы ГИС по их форме. Форма сигнала (извилистостоть, амплитудные отклонения) может быть проинтерпретирована и это знание позволит геологу выдвигать гипотезы относительно условий осадконакопления.

Сравнивать данные и их наборы необходимо с использованием метрик сранения - это мера, которую используют для измерения степени сходства , которая показывает насколько хорошо наборы данных соответствуют друг другу.

Сравнение распределений

Математическое ожидание (среднее значение) и дисперсия являются двумя основными метриками, используемыми для оценки степени сходства между двумя наборами данных.

Например, существуют две случайные выборки, внутри которых значения распределены по нормальному закону.

Untitled

Если сравнить их по средним значениям, то распределения будут одинаковыми. Однако, дисперсия (размах крыльев распределения) говорит нам о том, что краное распределение обладает большей неопределенностью.

💡 [Прочитайте статью](https://habr.com/ru/companies/skillfactory/articles/674880/) и сформулируйте для себя чек-лист проверки и сравнения распределений

Непараметрические тесты

Непараметрические тесты — это методы проверки гипотез без использования статистики. Они основаны на проверке отдельных случаев или групп случаев вместо средних значений или дисперсий. Частично, эти тесты были описаны в предыдущем разделе.

Непараметрические тесты могут быть полезны для проверки небольших объемов данных или когда стандартные методы статистического анализа не применимы. Однако они не всегда дают точные результаты и должны использоваться с осторожностью.

💡 [Прочитайте статью](https://machinelearningmastery.ru/nonparametric-statistical-significance-tests-in-python/)

Допустим, перед вами стоит задача проверить два набора данных на принадлежность к одному и тому же распределению. Давайте сгенерируем две случайных выборки с различной дисперсией и средним значением, при этом закон распределения в обоих случаях должен быть нормальным.

sample1 = np.random.normal(loc=80, scale=10, size=300)
sample2 = np.random.normal(loc=10, scale=35, size=300)
sns.histplot(sample1,bins = 10,color='red')
sns.histplot(sample2,bins = 10,color = 'green')

Untitled

Полученный результат наглядно демонстрирует, что две выборки не относятся к одному распределению (нулевая гипотеза не подтверждается). Если выполнить тест Колмагорова-Смирнова, то мы получим количественное подтверждение наблюдаемой непохожести на графике

ks_2samp(sample1, sample2)
>> KstestResult(statistic=0.91, pvalue=7.683562520124444e-133)

А теперь, для наглядости, сгенерируем две очень похожие случайные выборки и выполним тест:

sample1 = np.random.normal(loc=10, scale=11, size=300)
sample2 = np.random.normal(loc=10, scale=10, size=300)
sns.histplot(sample1,bins = 10,color='red')
sns.histplot(sample2,bins = 10,color = 'green')

Untitled

ks_2samp(sample1, sample2)
>> KstestResult(statistic=0.08333333333333333, pvalue=0.2488247843957968)

Значение p-value больше 0.05 и это значит, что нулевая гипотеза подтвержается и две выборки происходят из одного и того же распределения. Это значит, что они похожи.

Корреляция Пирсона

Корреляция Пирсона — это мера связи между двумя переменными. Она вычисляется путем умножения отношения значений корреляции между двумя переменными на квадрат расстояния между ними. Корреляция Пирсона является одной из самых распространенных мер связи между переменными. Значение корреляции Пирсона обычно выражается числом от -1 до 1, где 0 означает отсутствие связи, -1 обозначает, что связь противоположна, а 1 означает полную связь между переменными. Чем выше значение корреляции Пирсона, тем более тесная связь между переменными наблюдается.

Ниже приведен слайд, демонстрирующий частный случай использования корреляции Пирсона для оценки схожести сейсмотрасс.

Untitled

Важно отметить, что значение корреляции Пирсона не всегда указывает на причинно-следственные связи. Иногда высокая корреляция может указывать на наличие положительной обратной связи между двумя переменными, которая может привести к увеличению одной из них без увеличения другой. В других случаях низкая корреляция может указывать на отсутствие связи между переменными.

💡 **Задача**: Используя датасет `id-features.xlsx` , для столбцов c 25 по 35, рассчитайте матрицу коэффициентов корреляции между всеми фичами (столбцами) и постройте [тепловую карту](https://www.notion.so/bb-python-1428eaff9a2347a6b20a4fc41893663d). Для расчета матрицы используйте функцию `pd.corr()` и метод `pearson`

Метрики расстояний

В общем смысле, чем меньше расстояние между объектами/векторами/точками в пространстве, тем большей похожестью они обладают

Оценка схожести через расстояние — это метод, который используется для определения того, насколько сильно две выборки данных похожи друг на друга. Этот метод основывается на оценке расстояния между двумя наборами данных и сравнении результатов с помощью линейной функции. Оценка схожести через расстояние может быть использована для различных целей, включая:

  1. Оценка качества модели машинного обучения: этот метод используется для оценки качества модели машинного обучения, которая была обучена на одной группе данных и применена к другой группе данных.
  2. Оценка точности классификации: этот метод используется для оценки точности классификации объектов на основе их признаков.
  3. Оценка значимости различий: этот метод используется для оценки значимости различий между двумя наборами данных, таких как различия в доходах или уровне образования. В целом, оценка схожести через расстояние является полезным инструментом для анализа больших объемов данных и определения того, насколько сильно две выборки связаны друг с другом.
💡 Подробный и наглядный материал с каринками описан [здесь](https://towardsdatascience.com/17-types-of-similarity-and-dissimilarity-measures-used-in-data-science-3eb914d2681). Прочитайте статью.

Оценить расстояние или похожесть двух векторов (под вектором понимается список значений характеристик объекта, например каждая строка в датасете id-features.xlsxсчитается вектором фичей) можно различными способами. Выбор того или иного способа зависит от задачи, которую нужно решить.

💡 В своей практике используйте вседоступные меткрики, но выбирайте те, которые позволяют получать интерпретируемый результат, относительно вашей задачи.

Евклидово расстояние — это геометрическое расстояние в многомерном пространстве. Оно вычисляется по теореме Пифагора. Пусть в n-мерном пространстве заданы две точки: p(p_1,p_2,…p_n ) и q(q_1,q_2,…q_n). Тогда евклидово расстояние между ними вычисляется по следующей формуле:

Untitled

Однако использование евклидова расстояния имеет существенный недостаток: если два временных ряда одинаковы, но один из них незначительно смещен во времени (вдоль оси времени), то евклидова метрика может посчитать, что ряды отличаются друг от друга.

Расстояние DTW

В качестве одной из метрик сравнения сигналов можно использовать метод динамического преобразования временной шкалы (DTW-алгоритм), который позволяет найти оптимальное соответствие между последовательностями. DTW-алгоритм является удобным и надежным инструментом, поскольку он способен сравнивать сигналы различной длины и дискретности (наиболее частый случай в практике), при этом сигналы могут быть смещены относительно друг друга по временной (глубинной) шкале.

Ниже приведен пример реализации DTW-алгоритма для решения задачи группирования каротажных данных по форме сигнала.

Untitled

Слева на картике показан результат группирования каротажных кривых на карте и типовые(характерные) сигналы для каждого кластера. Срава показана референсная модель (вид сбоку и вид сверху). Применение DTW позволило достаточно хорошо воспроизвести исходную модель с разделением сигналов по группам, согласно их форме.

Информационная энтропия

Оперируя в своей работе системами в вероятностном домене (“вероятность” или “вероятностная модель”), можно выполнять процедуру их сравнения через оценку энтропии. В общем смысле, информационная энтропия используется в теории информации как мера, которая характеризует беспорядок системы, т.е. ее неопределенность. Энтропия будет минимальной, когда мы имеем нулевую информацию об объекте или же когда мы изучили объект на 100%.

Untitled

Максимальная энтропия будет в случае, если вероятность события 50%.

Чем выше информационная энтропия, тем более сложно или неоднозначно определить состояние системы или явления. Это может привести к ошибкам в прогнозах и принятии решений на основе неполных или неточных данных.

Например, при решении задачи кластеризации (метод Fuzzy C-Means) или классификации, результатом/прогнозом для каждого образца/примера будет вектор вероятности принадлежности образца/примера к кластеру/классу. Если посчитать значение энтропии для каждого прогноза (вектора вероятности), то можно увидеть наиболее стабильные прогнозы, а также наиболее неопределенные.

Метрики соответствия

MSE (Mean Squared Error, среднеквадратичная ошибка) – измеряет среднеквадратичную разность между расчетными значениями и фактическими. Для каждой точки вычисляется квадратная разница между расчетными значениями и фактическими, а затем эти значения усредняются.

Untitled

где yᵢ фактический значение, а ŷᵢ расчетное. В силу свойств этой метрики, усиливается влияние ошибок, по квадратуре от исходного значения. Даже одно большое отклонение между фактическим и расчетным значением может сильно исказить метрику. Чувствительна к выбросам и к выборкам где разброс значений очень большой. В зависимости от данных может оказаться невозможным получить очень маленькое значение для среднеквадратичной ошибки.

MAE (Mean Absolute Error, средняя абсолютная ошибка) – это средняя абсолютная разность между расчетным значением и фактическим.

Untitled

где yᵢ фактическое значение, а ŷᵢ расчетное. В отличие от среднеквадратических ошибок, где используется квадрат разности, MAE является линейной оценкой, поэтому вес разностей одинаков независимо от диапазона. Например, ошибка между 2 и 4 будет в два раза меньше, чем между 4 и 8, т.е. 2 и 4 соответственно (в то время как при квадратичной оценке 4 и 16, т.е. в 4 раза меньше). Менее чувствительна к выбросам.

MSE и MAE используют ту же шкалу, что и измеряемые данные.

R2 (Коэффициент детерминации) – представляет собой часть дисперсии временного ряда, объясняемую другим временным рядом. Единица минус отношение средней квадратичной ошибки к средней квадратичной ошибке среднего значения выборки (дисперсии).

Untitled

Где: R^2 - коэффициент детерминации, MSE - средняя квадратичная ошибка, σ^2- дисперсия выборки, y_t - фактическое значение, y ̂_t - расчетное значение, y ̅_t - среднее фактическое значение. Принимает значения от -∞ до 1. Чем ближе значение коэффициента к 1, тем сильнее зависимость.

Test your mind

Задачи после прохождения курса

ML инструментарий

Препроцессинг: нормировка - в разработке

Понижение размерности (PCA, tsne): - в разработке

make discretized color map from continuous

from matplotlib import cm
from matplotlib.colors import LinearSegmentedColormap

def cmap_discretize(cmap, N):
        
    '''
    The function transforms continuous cmap to discretized cmap with unique color        
    return discretized cmap 
    '''
    colors_i = np.concatenate((np.linspace(0, 1., N), (0.,0.,0.,0.)))
    colors_rgba = cmap(colors_i)
    indices = np.linspace(0, 1., N+1)
    cdict = {}
    for ki,key in enumerate(('red','green','blue')):
        cdict[key] = [ (indices[i], colors_rgba[i-1,ki], colors_rgba[i,ki]) for i in range(N+1) ]        
    # Return colormap object.

    return LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)

cmap_discretize(cm.seismic,5)

About

Хакатон от Газпрома

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published