7.36K subscribers
1.72K photos
72 videos
1 file
1.28K links
Привет! Мы — образовательная платформа в сфере аналитики Simulative: simulative.ru

Создаём курсы-симуляторы, где обучаем не на «апельсинках», а на кейсах из реального бизнеса.

Наш уютный чат: @itresume_chat
Поддержка: @simulative_support
Download Telegram
🔥 Создание поля объектов в Python с помощью setattr()

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

Код, который вы напишете для этой задачи, должен уметь обрабатывать различные словари со своим набором ключей и значениями. Неординарная задача. Но мы знаем, как это сделать!

🟢 Один из способов решения этой задачи - использовать функцию setattr(). Эта функция позволяет динамически назначать поля объекта во время выполнения программы. Она принимает три аргумента: объект, имя атрибута и значение атрибута.

Например, предположим, что у нас есть словарь my_dict:

my_dict = {
    'name': 'Максим',
    'age': 25,
    'city': 'Москва'
}

Мы можем создать объект Person и использовать функцию setattr() для присвоения значений атрибутам:

class Person:
    pass

p = Person()
for k, v in my_dict.items():
     setattr(p, k, v)

В результате мы получим объект Person с атрибутами name, age и city.

📎 Такой подход кажется крайне удобным, потому что мы автоматизируем создание атрибутов и избегаем ручной обработки.

💡 Кроме того, функция setattr() может пригодиться в других ситуациях, когда нужно динамически создавать поля объекта. Например, при работе с API или базами данных, когда набор полей может меняться в зависимости от запроса пользователя.

‼️ Чтобы освоить или улучшить свои навыки программирования на Python, присоединяйтесь к нашему бесплатному курсу!

#python
🔥12👍7
🤨 Действительно ли параметр inplace в Pandas ускоряет операции?

Многие пользователи Pandas считают, что использование операций с параметром inplace=True ускорит работу с датафреймами. Согласны ли вы с этим? Всегда ли это работает?

🟢 Давайте это проверим

Inplace - это параметр методов Pandas, который позволяет изменять DataFrame без создания нового объекта. Звучит так, будто это должно работать быстрее, но есть некоторые нюансы:

- Inplace, вопреки тому, что следует из названия, часто не препятствует созданию копий. Это первый пункт против скорости подобного способа.
- SettingWithCopy - часто получаемое предупреждение - оно не влияет на выполнение кода. Однако же получается Pandas выполняет дополнительные проверки, чтобы убедиться, что датафрейм изменен корректно. Соответственно, второй пункт против скорости inplace.
- И последнее - inplace операции могут быть менее гибкими, так как не позволяют объединить несколько методов в одной строке.

Но это лишь предположения, давайте убедимся в них!

🔵 Мы провели небольшой анализ

Смотрите в карточке под постом как некоторые методы работают с inplace.

❗️Какие-то методы действительно работают быстрее, некоторые гораздо быстрее, но все-таки большинство - медленнее.

Поэтому, перед тем, как применять inplace, обязательно оцените время и потенциальную пользу в вашем конкретном случае и не потеряйте важные данные!

🧠 Если хотите все проверить самостоятельно - делимся ссылкой на коллаб.

#python #pandas
🔥5
🔥 Переменная цикла и области видимости

Не так давно у нас был пост о том, что переменную цикла нельзя изменить внутри for-loop. Давайте продолжим тему переменной цикла, но взглянем на нее под другим углом!

🤔 Посмотрите на блоки кода ниже и ответьте, что вы получите после выполнения?

a = 1
for a in range(6):
pass

print(a)

------------------------

a = 1
[... for a in range(6)]

print(a)

Итак, в первом случае получим 5, а во втором - 1. То есть цикл for обновил существующую переменную a, но генератор списка этого не сделал. Итак, вопрос:

Почему переменная цикла обрабатывается по-разному в for-loops и list cоmprehension?

🟢 В цикле for переменная цикла как бы «утекает» в окружающую область видимости. Другими словами, как только цикл завершится, вы все равно сможете получить доступ к переменной цикла. Убедимся:

for loop_var in range(6):
pass

print(loop_var)
# 5

Мы могли бы написать даже что-то такое:

b = 1
for a in range(6):
pass

print(a, b)
# 5 1

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

🟠 Но генераторы списков так не работают. Вместо этого переменная цикла всегда остается локальной для генератора списка. Она никогда не утекает «наружу».

[... for loop_var2 in range(6)]

print(loop_var2)
# NameError: name 'loop_var2' is not defined

Вот почему существующая переменная a, которая также использовалась внутри генератора списка, осталась неизменной. Генератор списка определил переменную цикла (a), локальную для его области видимости.

#python
🔥10👍4🤩2
💥 3 лайфхака по считыванию csv-файла в Python 💥

Нам, аналитикам, часто приходится работать с csv-файлами. Все-таки это удобный способ хранения больших наборов данных и дальнейшей работы с ними.

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

🔵 Первый лайфхак

Иногда при больших исходных данных работать приходится только с некоторым количеством из них. И если заранее известно, что из условно 10 миллионов строк вам будет достаточно только 1000, задайте в read_csv параметр nrows. Это позволит сэкономить и память, и время.

В противном же случае пришлось бы выполнить сразу несколько действий: загрузить весь сет, взять от него срезом только первую 1000 строк, и удалить первоначальный. Один параметр вместо трех строк. Удобно!

#Было
df = pd.read_csv('file.csv')
new_df = df[:1000].copy()
del df
_______
#Стало
df = pd.read_csv('file.csv',
nrows = 1000)

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

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

#Было
df = pd.read_csv('file.csv')
df = df[['col1', 'col2',
'col3']]
________
#Стало
df = pd.read_csv('file.csv',
usecols = ['col1', 'col2',
'col3'])

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

То есть если вы передадите список [0, 1, 2], то будут пропущены первые три строки:

df = pd.read_csv('file.csv', 
skiprows = [0, 1, 2])

💡 Более того, вы можете не просто пропустить несколько идущих подряд строк, вы можете передать в skiprows функцию, которая определит, какие строки необходимо сохранить, а какие можно пропустить. 

Например, известно, что в файле каждая вторая строка пустая, просто передайте в skiprows лямбда-функцию:

df = pd.read_csv('file.csv', 
skiprows=lambda x:
(x % 2) != 0))

В нашем 👉 Симуляторе «Аналитик данных» 👈 в модуле по Python эти уловки вам точно пригодятся, ведь вы будете работать с файлами (логами, выгрузками и таблицами) из реального бизнеса от наших партнеров!

#python #pandas
🔥17👍8
🔥 Избегайте этого при работе с Pandas

Как вы выбираете элементы при работе с датафреймами? Сначала строку, а потом столбец или столбец, а за ним строка? А может вы вовсе определяете порядок наугад? Если так, то вы упускаете огромный потенциал производительности! Скорее читайте наш пост.

🔵 При индексации датафрейма важно определить порядок выбора - сначала столбец или сначала строки, так как это существенно влияет на время выполнения операций.

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

# сначала выбираем столбец, затем строку
%timeit df['col']['row']
#2.96 µs

# наоборот - строка, затем столбец
%timeit df.loc['row']['col']
#45.4 µs

Почему это происходит?

Pandas DataFrame - это структура данных, состоящая из столбцов, и последовательные элементы в каждом столбце хранятся рядом друг с другом в памяти.

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

# выбираем столбец (смежные блоки памяти) 
%timeit df['my_column']
# 1.73 µs

# выбираем строку (несмежные)
%timeit df.iloc[0]
# 38.4 µs

В примере выше, извлечение более 32 миллионов элементов столбца оказалось более чем в 20 раз быстрее, чем извлечение всего девяти элементов, хранящихся в строке.

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

🟣 Напротив, если мы выбираем сначала столбец, то элементы извлекаются из смежных блоков памяти, что работает гораздо эффективнее. Кроме того, столбец по своей сути уже является серией Pandas, и нет никаких расходов на его преобразование.

Так что запоминайте: сначала столбец, а потом строка 💡


Присоединяйтесь к нашим студентам 👉 Симулятора «Аналитик данных» 👈, чтобы не просто уметь решать рабочие кейсы, а знать, как это делать оптимально!

#python #pandas
🔥22👍8
🔥 А вы активно используете match/case в Python?

Это достаточно новая конструкция, введенная в Python 3.10, которая позволяет нам написать более читаемый и лаконичный код при обработке различных вариантов значений. Из-за ее «молодости» не все о ней знают, но она может быть очень полезна и даже удобнее, чем if/else. Давайте проверим!

🟢 Что такое match/case?

Это способ сопоставления значения с набором шаблонов и выполнения соответствующего блока кода для первого подходящего шаблона. Подобное по функциональности можно встретить в других языках программирования под названием switch/case. Однако в Python match/case стал еще мощнее и гибче.

Давайте рассмотрим простой пример использования:

def http_status(status_code):
match status_code:
case 200:
return "OK"
case 400:
return "Bad Request"
case 404:
return "Not Found"
case 500:
return "Internal
Server Error"
case _:
return "Unknown Status"

print(http_status(200))
# OK

print(http_status(400))
# Bad Request

print(http_status(404))
# Not Found

print(http_status(500))
# Internal Server Error

print(http_status(403))
# Unknown Status

Здесь мы определяем функцию http_status, которая принимает аргумент status_code. С помощью конструкции match мы проверяем значение status_code и возвращаем соответствующий текстовый статус для первого подходящего шаблона. Если ни один из шаблонов не подходит, мы используем шаблон _, чтобы вернуть "Unknown Status".

🔵 А что такое шаблон _?

Шаблон _ представляет собой своего рода «по умолчанию» в конструкции match/case. Если ни один из описанных шаблонов не совпадает с переданным значением, то выполняется блок кода, соответствующий шаблону _. Это позволяет предусмотреть обработку всех возможных вариантов, даже тех, которые не перечислены явно.

Согласитесь, что match/case делает наш код более легким для чтения. Мы можем сразу видеть все возможные варианты значений и обрабатывать их отдельно.

🌟 И это только начало! Match/case в Python имеет еще много интересных возможностей, которые мы рассмотрим в следующем посте. Он позволяет избежать громоздких проверок типов данных, длины и других условий, делая наш код гораздо более компактным!

А вы уже пробовали использовать match/case в Python? Расскажите о своем опыте в комментариях!

#python
🔥16👍53
🔍 Быстро и надежно: Избегаем проблем с NumPy! 🔍

Аналитики знают, что NumPy - это мощный инструмент для численных вычислений. Однако, существует одна тонкость, о которой стоит помнить.

Если в массиве NumPy присутствует значение NaN (Not a Number), некоторые агрегатные функции могут вернуть неожиданный результат. И это может оказаться совсем не то, чего вы ожидаете!

import numpy as np
arr = np.array([1, 2, 3, np.NaN])
np.sum(arr) # nan
np.mean(arr) # nan
np.max(arr) # nan

Но не беспокойтесь, у нас есть решение! 👇

🔵 Во-первых, хорошей практикой является проверка массива на наличие значений NaN и их замена, например, на 0. Это поможет избежать проблем в большинстве случаев.

🟠 Однако, есть ещё один способ избежать проблем, который предлагает сам NumPy. Для тех, кто хочет быть на высоте, существуют специальные методы, нечувствительные к значениям NaN! 🚀

Например, вместо использования функций np.sum, np.mean, np.max и др., вы можете воспользоваться np.nansum, np.nanmean, np.nanmax и другими аналогичными методами.

import numpy as np
arr = np.array([1, 2, 3, np.NaN])
np.nansum(arr) # 6.0
np.nanmean(arr) # 2.0
np.nanmax(arr) # 3.0

Их особенностью является то, что они игнорируют значения NaN в массиве и возвращают результат, как будто эти значения не существуют.

Теперь, даже если где-то в вашем массиве присутствует хотя бы одно значение NaN, это уже не повлияет на все остальные вычисления! Вы получите верные и надежные результаты! 💪

💬 Пишите в комментариях, если вам пригодился этот совет или если у вас есть свои хитрости по работе с NumPy! 📚

#python #numpy
🔥24👍61
🔥 Match/case со списками и словарями

Признавайтесь, ждали ли вы наш заключительный пост о нашем новом знакомом - match/case? Сегодня испробуем, как он может работать со списками и словарями.

🟢 Надеемся вы уже убедились, что возможности у match/сase огромные. И они далеки от обычного if/else. Со списками, например, мы можем сопоставлять индексы, значения и даже длину списка! Например:

- Используем _ в качестве wildcard (и мы можем добавлять их сколь угодно много)
- Мы можем извлечь значения, присвоив им имя (case [i1, i2, i3])
- *_ звездочка-подчеркивание используется, чтобы брать все оставшиеся элементы

Смотрите код, который у нас получился, на карточке 1 😉

🔵 Что еще можно сделать?

Мы также можем следить за расположением элементов. Например подберем:

- Любой список, начинающийся с А
- Любой список из 3 предметов, начинающийся с Б, а затем А ИЛИ С
- Любой список, где второй элемент с конца - Д

Код с фруктами на карточках 2-3 😉

🟣 Можем ли мы мэтчить словари?

Конечно да! Мы даже можем быть очень точными в наших мэтчах. Например:

- Смэтчить любой словарь с ключом name
- Смэтчить любую str, если она состоит из заглавных букв

Код на карточке 4!

💡 Обратите внимание, что мы проверяем типы, добавляя скобки () после типа класса. И несмотря на то, что мы использовали только str(), это также будет работать и для int().

🔥 Вот такие удивительные вещи может match/case! Расскажите, приходилось ли вам уже использовать его в работе, или вы раньше не были знакомы с этой конструкцией? Или может вы думали, что это простой switch/case? 🤪

#python
🔥9👍3
🔍 Целостность данных при merge в Pandas

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

Но есть решение! У метода merge() есть параметр validate, который проверяет, имеет ли merge определенный тип.

🔹 Объединение с Уникальными Ключами (One-to-One Merge)

При таком объединении ключи должны быть уникальными в обеих таблицах. Давайте посмотрим на пример:

import pandas as pd

df1 = pd.DataFrame({
'colA': ['A', 'B', 'C'],
'colB': [1, 2, 3]})

df2 = pd.DataFrame({
'colA': ['A', 'B', 'C'],
'colC': [4, 5, 6]})
# ключи уникальны

result = pd.merge(df1, df2,
on='colA',
validate='one_to_one')
print(result)

Все получилось и мы не получим никакой ошибки:

  colA  colB  colC
0 A 1 4
1 B 2 5
2 C 3 6

🔹 Обработка Повторяющихся Ключей

Если в одной из таблиц есть повторяющиеся ключи, merge выдаст ошибку:

import pandas as pd

df1 = pd.DataFrame({
'colA': ['A', 'B', 'C'],
'colB': [1, 2, 3]})

df2 = pd.DataFrame({
'colA': ['A', 'B', 'B'],
'colC': [4, 5, 6]})
# повторяющиеся ключи:
'colA': ['A', 'B', 'C'] и
'colA': ['A', 'B', 'B']

try:
result = pd.merge(df1, df2,
on = 'colA',
validate='one_to_one')
except pd.errors.MergeError as e:
print(f"Ошибка: {e}")

Результат:

Ошибка объединения: Merge keys are not unique in right dataset; not a one-to-one merge

🔹 Какие еще есть объединения?

- one_to_one: Ключи должны быть уникальными в обоих датафреймах
- one_to_many: Ключи должны быть уникальными в левом датафрейме.
- many_to_one: Ключи должны быть уникальными в правом датафрейме.
- many_to_many: Ключи могут быть уникальными, а могут и не быть в обоих датафреймах

И Pandas выдаст ошибку слияния, если merge не соответствует указанному типу.

Это поможет вам предотвратить ошибки, которые часто могут остаться незамеченными.

#python #pandas
👍20🔥81
🔥 Супер-удобная функция Matplotlib

Это, должно быть, самая крутая вещь, которую мы когда-либо узнавали о Matplotlib.

🤔 Обычно, для создания графиков мы используем метод plt.subplots(). Но иногда это превращается в довольно громоздкие конструкции. А еще:

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

💡 Вместо этого мы можем использовать метод plt.subplot_mosaic(). Так, мы сможем создать график и расположить его как угодно, определив структуру графика простой строкой.

Например, расположение строк:

- AB
- AC

Создаст три подграфика, в которых:

- подграфик A будет занимать весь первый столбец
- подграфик B будет занимать верхнюю половину второго столбца
- подграфик C будет занимать нижнюю половину второго столбца

ax = fig.subplot_mosaic("""AB
AC""")

ax['A'].plot(...) # область А
ax['B'].plot(...) # область B
ax['C'].plot(...) # область C
Результат смотрите под постом

Затем создайте подграфик в определенной области, просто через доступ к словарю ax по ключу ('A', 'B' или 'C' ).

type(ax)
dict

ax['A'].plot(...) # область А
ax['B'].plot(...) # область B
ax['C'].plot(...) # область C

📌 Попробуйте вместе с нами!

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(
layout="constrained",
figsize = (8,6))

mosaic = """abc
add
eee"""

ax = fig.subplot_mosaic(mosaic)
x = np.linspace(0.1, 1, 10)

ax['a'].plot(x, np.sqrt(x))
ax['b'].scatter(x, x**2)
ax['c'].scatter(x, np.log(x))
ax['d'].bar([1,2,3,4,5],
[1,2,3,4,5])
ax['e'].hist(
np.random.normal(0,1, 100))

plt.show();

Скажите, классная функция?! С помощью нее гораздо проще создавать дашборды 😉

#python #visualization
🔥13😱7🤩5
🔥 Базовая Шпаргалка по Pandas 🔥

Pandas - один из самых основных и важных инструментов для работы с данными в Python. Он предоставляет множество удобных функций для загрузки, обработки, и анализа данных.

Мы собрали шпаргалку, в которой все основные функции, которые пригодятся вам в повседневной работе с Pandas:

Импорт данных: Загрузка данных из разных источников.

Исследование данных: Ознакомление с данными, просмотр первых и последних строк, получение информации о типах данных.

Извлечение информации из датафреймов: Выбор столбцов и строк, выполнение различных операций.

Добавление и удаление данных: Добавление новых столбцов и строк, удаление лишних данных.

Фильтрация: Выбор данных по условию, множественные условия.

Сортировка: Сортировка данных по столбцам, множественная сортировка.

Агрегирование: Группировка данных, вычисление статистических показателей, подсчет уникальных значений.

И для удобства мы сделали для вас notebook 😊

Сохраняйте и пользуйтесь 👇

https://colab.research.google.com/drive/1XOF8oQB3kzs0Yxt17XdtGy8IFNudvp4c?usp=sharing

Если у вас есть какие-либо дополнения, не стесняйтесь оставлять в комментариях! 🐼

#python #pandas
👍15🔥86
🔥 Базовая Шпаргалка по Работе с Файлами в Python 🔥

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

Открытие файла для чтения
Чтение по строкам
Запись в файл
Добавление в файл
Чтение и запись в бинарных файлах
Работа с контекстом
Проверка наличия файла
Удаление файла
Создание директори
Список файлов в директории
Использование библиотеки shutil
Использование библиотеки os

И для удобства мы сделали для вас notebook 😊

Сохраняйте и пользуйтесь 👇
https://colab.research.google.com/drive/1CqADXAdImiAQ9VIYYzYGdwIuiXQrAF7K?usp=sharing

Если у вас есть какие-либо дополнения, не стесняйтесь оставлять в комментариях!

#python
👍11🔥73
📊 Работа с пропущенными данными - Восстановление и удаление данных

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

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

Давайте сначала создадим датафрейм для примера (см. в карточках под постом)

Как обработать пропущенные данные?

Каждая ситуация уникальна, и подход к ней должен быть индивидуальным. Давайте начнем с восстановления данных.

1 Способ: Восстановление данных

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

# Заполнение пропущенных значений на основе дополнительной информации
df.loc[df['Модель'] == 'Outback', 'Цена'] = 17625
df.loc[df['Модель'] == 'Civic', 'Потери'] = 150
df.loc[df['Модель'] == 'Mustang', 'Тип топлива'] = 'Дизель'

Результат смотрите в карточках

2 Способ: Удаление данных

Но, к сожалению, не всегда данные можно восстановить из источника.

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

Этот способ будет актуальным, если у вас много данных и небольшое количество пропусков.

# Удаление строк с пропущенными значениями в столбце "Цена"
df = df.dropna(subset=['Цена'])

Результат смотрите в карточках

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

Сегодня мы разобрали, как заполнять пропущенные данные и удалять их. А когда наберем 25 👍 , подробно расскажем вам про замену средним и случаи, когда стоит оставить пропуски 😉

#python #pandas
👍25🔥3😁2
🎩🐍 Одиночка в Python: Магия Singleton-паттерна🪄

Паттерн Singleton - это настоящий гений в мире проектирования классов. Он позволяет нам создать только один экземпляр класса и обеспечить глобальную точку доступа к этому экземпляру. Имея такую возможность, мы можем контролировать доступ к общему ресурсу и гарантировать, что всегда используется один и тот же объект.

Один из способов реализации Singleton в Python - использовать магический метод __new__. Метод __new__ вызывается при создании нового экземпляра класса и отвечает за его создание.

Давайте посмотрим на пример:

class Singleton:
instance = None

def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = super().__new__(cls, *args, **kwargs)
return cls.instance

singleton1 = Singleton()
singleton2 = Singleton()

print(singleton1 is singleton2) # True


В этом примере мы создали класс Singleton и объявили переменную instance, которая будет хранить единственный экземпляр класса.

Метод __new__ проверяет, существует ли экземпляр класса. Если нет, то он создает новый экземпляр и сохраняет его в переменную instance. При последующих вызовах Singleton() будет возвращаться тот же самый экземпляр. Таким образом, мы гарантируем, что всегда используется только один экземпляр класса.

🤔 Для чего мы используем паттерн Singleton в нашей работе:

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

❗️ Но будьте осторожны!

Иногда применение Singleton может быть нецелесообразным или даже привести к проблемам. Например Singleton может усложнить расширение функциональности класса, так как он предоставляет только одну точку доступа к объекту.

Подобные тонкости, лайфхаки и фишки мы более подробно разбираем у нас в Симуляторе "Аналитик данных" 🔥

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

#python #ООП
👍114🔥2👎1
🪄 Волшебство магических методов в Python

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

Один из первых магических методов, с которым мы сталкиваемся - это __init__. Он вызывается при создании нового объекта класса и позволяет инициализировать атрибуты объекта.

class Cat:
def __init__(self, name):
self.name = name

def __str__(self):
return f"Я кот {self.name}"

cat = Cat("Буся")
print(cat)
# Я кот Буся

Здесь мы создали класс
Cat и определили метод __init__, который принимает аргумент name и устанавливает его в атрибут name.

Мы также переопределили метод __str__, чтобы получать информативное строковое представление объекта при его выводе.

Магический метод str используется для определения текстового представления объекта. Этот метод вызывается, когда вы пытаетесь преобразовать объект в строку с помощью встроенной функции str() или когда объект используется в контексте, где ожидается строковое представление, например, при выводе на экран с помощью print().


Еще одним распространенным магическим методом является __repr__. Он похож на __str__, но предназначен для возвращения представления объекта, которое можно использовать для его точного воссоздания. Обычно используется для отладки и представления объектов в консоли.

class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __repr__(self):
return f"Point({self.x}, {self.y})"

point = Point(2, 3)
print(point) # Point(2, 3)


Здесь класс Point представляет точку на плоскости, и метод __repr__ возвращает строку, содержащую код для создания точки, что точно пригодится для отладки.

Магические методы играют важную роль и при сравнении объектов операторами сравнения ( ==, <, >). Для каждого из таких операторов есть свой магический метод.

class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height

def __eq__(self, other):
return self.width == other.width and self.height == other.height

def __lt__(self, other):
return self.area() < other.area()

def area(self):
return self.width * self.height

rect1 = Rectangle(4, 5)
rect2 = Rectangle(4, 5)
rect3 = Rectangle(3, 6)

print(rect1 == rect2) # True
print(rect1 < rect3) # False


В этом примере мы переопределили метод __eq__, чтобы сравнивать два прямоугольника на основе их ширины и высоты, и метод __lt__, чтобы сравнивать их по площади.

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

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

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

Магические методы в Python предоставляют мощные возможности для определения поведения объектов и помогают создавать чистый и читаемый код. Пользуйтесь ими с умом! 🎩🐍

#python
10🔥6😢2👍1
3 полезных функции для работы с Pandas 🔥

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

Сегодня мы рассмотрим несколько очень удобных, но при этом неочевидных методов Pandas: stack, set_option, insert.

Для начала создадим таблицу, которая содержит название города, среднюю температуру в °C, скорость ветра в м/с

import pandas as pd
multi_col = pd.MultiIndex.from_tuples(
[('Temperature', '°C'),
('Wind', 'm/s')])

df = pd.DataFrame(columns=multi_col,
index=['Тюмень','Москва','Краснодар'],
data=[[5, 2], [10, 5], [20, 1]])



1️⃣ STACK


Давайте теперь переформатируем эту таблицу так, чтобы строки были разбиты не только по городам, но еще и по типу измерения (Температура/Скорость ветра).

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

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

В stack есть параметр level - уровни стекинга. Список «уровней», которые мы хотим перекинуть с оси столбцов на ось индекса. Например, если передать только 0 уровень, то в ось индекса попадут Типы измерений.

df.stack(level = 0)


Если же передать список уровней [0, 1], то все уровни с оси столбцов перейдут в индекс.

df.stack(level = [0, 1])


2️⃣ SET_OPTION


Каждый раз при просмотре большого датафрейма приходится писать df.head(5), чтобы вывести только первые строки!


Знакомая ситуация? Думаем, да.

Чтобы сделать работу с Pandas более приятной и избавиться от дубляжей в коде, можно воспользоваться методом set_option. Он позволяет заменить стандартные настройки Pandas на свои кастомные, настроив отдельные аспекты под себя.

Для начала рекомендуем прописать все необходимые конфигурации. Например:

display_settings = {
# max кол-во столбцов для отображения
'max_columns': 10,
# max кол-во строк для отображения
'max_rows': 1,
# количество цифр знаков после запятой
'precision': 0,
# отображение полного размера датасета
'show_dimensions': True
}


После этого устанавливаем настройки с помощью set_option:

for op, val in display_settings.items():
pd.set_option("display.{}".format(op), val)


В таком случае выведется только 1 строка, нет цифр после запятой и присутствует полный размер датасета (внизу).

Все как заказывали 😄

Примечание: Чтобы отменить настройки, используйте reset_option:

pd.reset_option("all", silent = True)


3️⃣ INSERT


Еще одна знакомая до боли задача:

Добавить новый столбец в середину или начало существующего датафрейма.


Зачастую эту задачу решают так:

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

Этого всего можно избежать, использовав метод insert. Он позволяет добавлять столбец в определенном месте, используя индекс столбца.

Например, добавим новый столбец Скорость ветра в км/ч в самое начало нашего датафрейма:

df.insert(0, ('Wind_km', 'km/h'),
df[('Wind', 'm/s')]/1000*3600)

Все результаты команд смотрите в карточках под постом!)

Хотите прокачать свои навыки на реальных бизнес-кейсах? Записывайтесь на наш бесплатный курс по Pandas: simulative.ru/pandas

#python #pandas
👍14🔥73
🔥 Долой длинные строки в Python

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

Вот несколько случаев, где это встречается особенно часто:

* Длинные строки
* Большое количество аргументов в функции
*
method chaining в Pandas

Рассмотрим такой «длинный» код на примере Pandas. Согласитесь, выглядит жутко:

df = coderun[coderun["cohort"] >= "2022-01"].
groupby(["cohort", "language", "user_id"])["problem_id"].agg("nunique").
groupby(["cohort", "language"]).agg(np.mean).reset_index().pivot_table(
values="problem_id", columns=["language"], index=["cohort"])


А теперь давайте приведем код в порядок. Есть, как минимум, два метода борьбы с такими ситуациями.

🔵 Перенос строки с помощью обратного слеша


Достаточно поставить в конце строки обратный слеш и Python будет воспринимать весь написанный код как единую строку.

df = coderun[coderun["cohort"] >= "2022-01"] \
.groupby(["cohort", "language", "user_id"])["problem_id"] \
.agg("nunique") \
.groupby(["cohort", "language"]) \
.agg(np.mean) \
.reset_index() \
.pivot_table(values="problem_id", columns=["language"], index=["cohort"]


Сами видите - код стал в разы более читаемым и даже компактным.

🔵 Оборачивание круглыми скобками

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

df = (
coderun[coderun['cohort'] >= '2022-01']
.groupby(['cohort', 'language', 'user_id'])['problem_id']
.agg('nunique')
.groupby(['cohort', 'language'])
.agg(np.mean)
.reset_index()
.pivot_table(values='problem_id', columns=['language'], index=['cohort'])
)


На наш взгляд, этот вариант даже лучше - нет лишних символов и код выглядит еще более аккуратным.

- - - - -

Записывайтесь на наш бесплатный курс по Pandas: simulative.ru/pandas , чтобы прокачать свои навыки на реальных бизнес-кейсах!

#python #pandas
🔥22👍83
Использование else в циклах for в Python 🔥

Недавно мы проводили небольшой опрос и выяснили, что 68% опрошенных не знают, что в цикле FOR можно использовать конструкцию ELSE. Поэтому сегодня мы решили вам про это рассказать!

Как это работает

Все привыкли использовать ELSE с условием IF. Однако в Python блок ELSE можно использовать и с циклами FORWHILE тоже, кстати).

ELSE проверяет, каким образом завершился выход из цикла. Если цикл был прерван оператором BREAK или RETURN, то условие ELSE не сработает. Если же цикл закончился «естественным» образом, т.к. закончились элементы для дальнейшего перебора, то после цикла отработает блок ELSE.

Примеры

Приведем простой пример, где ELSE не сработает, потому что цикл был прерван оператором BREAK.

for i in range(3):
break
else:
print("No else!")


А вот еще один, но здесь уже ELSE сработает, потому что цикл завершился сам:

for i in range(3):
print(i)
else:
print("Yes else!")


Как применить на практике

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

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

#python
👍13🔥52