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

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

Наш уютный чат: @itresume_chat
Поддержка: @simulative_support
Download Telegram
🔥 Cекреты функции sorted() в Python

Итак, в Python существует функция sorted(), которая используется для сортировки данныx. sorted() упорядочивает данные таким образом, чтобы было легче провести анализ и извлечь нужную информацию. Например, в аналитике, сортировка клиентов по возрасту, территориальной принадлежности и другим критериям - далеко не редкая задача.

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

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

client_list = [
{'name': 'John', 'age': 25, 'income': 50000},
{'name': 'Mary', 'age': 30, 'income': 70000},
{'name': 'Bob', 'age': 35, 'income': 40000},
{'name': 'Alice', 'age': 27, 'income': 60000}
]

И мы хотим отсортировать этот список по возрастанию возраста. Мы не получим нужный результат если просто передадим наш список в sorted(). Нужно дополнительно указать ключ сортировки.

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

🟨 В нашем случае мы будем использовать лямбда-функцию, которая возвращает значение атрибута 'age' или 'income' каждого клиента в списке. Мы напишем key=lambda x: x['age'], чтобы указать, что мы хотим отсортировать список по возрастанию возраста клиентов. Если понадобится отсортировать список по другому атрибуту, то мы поменяем ключ на key=lambda x: x['нужный атрибут'].

Итак, используем лямбда-функцию, чтобы указать ключ сортировки. Она вернет значение 'age' для каждого клиента в списке:

sorted(client_list, key=lambda x: x['age'])

🟦 Аналогично, если вы хотите отсортировать список клиентов по убыванию их доходов - добавьте параметр reverse=True:

sorted(client_list, key=lambda x: x['income'], reverse=True)

🟪 Абсолютно таким же образом с помощью sorted() мы сможем отсортировать список объектов класса по некоторому атрибуту. Нам просто понадобится передать в key нужный нам атрибут: key=lambda x: x.age.

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

#python
🔥18👍52
🔥 Апгрейдим Pandas: создаем условные столбцы без лишних циклов

Кто работал с большими объемами данных в Pandas, наверняка знает, что обработать столбцы по условию может оказаться довольно затратной операцией. Мы часто слышим: используйте векторизацию! А что это, где это найти? Объясняем.

> Векторизация - это способ обработки данных, при котором операции применяются не поочередно к каждому элементу массива, а сразу ко всему массиву. Многие методы pandas, в общем говоря, уже векторизованы и вам не нужно об этом задумываться, НО!

Каким методом вы бы воспользовались для фильтрации столбца по условию?

🔵 Самый распространенный способ

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

Тем не менее apply() - это не что иное, как усовершенствованный цикл. И из-за этого теряется вся суть векторизации.

💡 Давайте покажем вам как можно создать условный столбец более эффективно - векторизованным способом:

Для начала создадим DataFrame с 10 миллионами случайных чисел от 0 до 1:

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.random((10**7,1)).round(2), columns = ['col1'])

🔴 Сразу посмотрим как поведет себя apply(). Определим функцию, которая для каждого числа будет возвращать 'Class A', если число больше 0.5, и 'Class B' в противном случае:

def assign_class(num):
if num > 0.5:
return 'Class A'
return 'Class B'

Передадим её в apply() и замерим время:

%timeit a = df.col1.apply(assign_class)

# 1.96 s ± 302 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

И чем больше будут данные, тем более печальным будет время. ☹️

🟢 Более удобный способ

Выход из ситуации - np.where(). Он позволяет обрабатывать все гораздо быстрее, как раз являясь одним из многих инструментов векторизации:

%timeit a = np.where(df['col1']>0.5, 'Class A', 'Class B')

# 282 ms ± 14.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Существенное преимущество налицо!

Базовый синтаксис: numpy.where(condition [, x, y]).

Condition - условие; если оно выполняется то будут выбраны элементы из x; в противном случае - если это false, будут взяты элементы из y.

🟣 Так что, если хотите обработать большие объемы данных в Pandas быстро и без лишних циклов, однозначно используйте np.where(). А мы, со своей стороны cделаем все возможное, чтобы с такими инструментами вас знакомить: в Симуляторе “Аналитик данных” в модуле Python мы 2 главы уделяем самым популярным библиотекам Python: Pandas и Numpy!

#python #pandas #numpy
🔥20👍5
🔥 Нестандартная ситуация со словарями

Мы знаем, что вы - любите Python, а еще больше любите необычные фишки. Но знали ли вы, что при работе со словарями могут возникать довольно не интуитивные ситуации?

Посмотрите, чему будет равен my_dict?

my_dict = {
1.0: 'One (float)',
1: 'One (int)',
True: 'One (bool)',
'1': 'One (string)'
}

# {1.0: 'One (bool)', '1': 'One (string)'}

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

Мы знаем, что ключи должны быть уникальными и неизменяемыми. В данном случае нет никаких сомнений в том, что 1.0, 1 и True имеют разные типы данных.

type(1.0), type(1), type(True)

# (float, int, bool)

Если посмотреть на их id, убедимся что и они различны:

id(1.0), id(1), id(True)

# (139644507527152, 139645051222320, 9758592)

Но дело не только в этом! Ключи должны быть еще хешируемы. Для поиска ключа, Python использует именно его хэш-значение, а не id. Значит посмотрим на их хэш-значения:

hash(1.0), hash(1), hash(True)

# (1, 1, 1)

Вот и получается, поскольку они имеют одно и то же хэш-значение, словарь рассматривает их как одни и те же ключи.

📎 Но обратите еще внимание на итоговые ключ и значение: ключ - 1.0, в то время как значение соответствует ключу True.

Как так получилось?

Дело в том, что сначала 1.0 добавляется в качестве ключа, а значение устанавливается 'One (float)'. Затем, добавляя ключ 1, python распознает его как эквивалентное хэш-значение. И значение, соответствующее 1.0, перезаписывается на 'One (int)', в то время как ключ (1.0) сохраняется как есть.

Наконец, при добавлении True снова получаем эквивалентность хэша с существующим ключом 1.0. И снова, значение, которое изначально было 'One (float)' и обновлено до 'One (int)' на предыдущем шаге, перезаписывается на 'One (bool)'.

А почему сохранен строковый ключ '1'?

Тут все просто: он имеет уникальное хеш-значение, отличное от других ключей.

💥 Надеемся, что этот пост дал вам чуть более полное понимание работы словарей. Приходите к нам на Симулятор "Аналитик данных", где мы с нуля обучаем SQL и Python и делимся полезными фишками!

#python
🔥20👍6
🔥 Возможно, вы не знали этого о циклах for в Python

Недавно поступил вопрос от наших студентов, которые недавно освоили for-loop в Python и принялись экспериментировать.

💡 Загадка

При использовании цикла for невозможно изменить переменную цикла и повлиять на итерацию:

for i in range(5):
print(i)
i = 10

#0
#1
#2
#3


Как видно, переменная i не меняется, хотя мы попытались ее переназначить внутри цикла.

🟡 На самом деле, когда мы используем for-loop в Python, никто, как правило, не имеет намерений изменить переменную цикла внутри него. Но можно догадаться, откуда растут ноги. 🙂 Возможно, так могли бы поступить те, кто уже знаком с другими языками программирования, например, С++ или Java.

Но в питоне for-loops не работают таким образом. Изменение переменной цикла не влияет на итерацию.

При каждой итерации цикла for Python извлекает следующий элемент из указанного итерируемого объекта (iterable) - например, range(5). Затем значение этого элемента присваивается переменной цикла - например, i.

То есть после выполнения range(5) или любого другого range(n), объект, созданный range, становится абсолютно независящим от изменений на протяжении итераций. Будто мы написали цикл: for i in [0, 1, 2, 3, 4].

🖋 А теперь комментарий от наших преподавателей

Если вам действительно необходимо переназначить переменную цикла, используйте while, о котором частенько забывают, как только знакомятся с for-loop:

i=0
while i < 5:
print(i)
i = 10

#0

Или:

i=0
while i < 5:
print(i)
i += 2

#0
#2
#4

Это довольно простая вещь, но о ней стоит помнить при разработке!

👉 Записывайтесь на наш бесплатный курс по Python, где вы с нуля освоите новый язык программирования!

#python
👍14🔥9
🔥 Тонкости функции head в Pandas

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

🟡 Но у head() есть нюанс. Если данные содержат повторяющиеся значения, head() просто вернет первые строки и не станет учитывать их. Показываем пример:

import pandas as pd

df = pd.DataFrame(
[['Игорь', 95],
['Яна', 100],
['Петр', 97],
['Иван', 95]],
columns = ['Имя', 'Оценка'])

df.sort_values('Оценка', ascending=False).head(3)

| | Имя | Оценка |
|---|-------|--------|
| 1 | Яна | 100 |
| 2 | Петр | 97 |
| 0 | Игорь | 95 |

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

🖋 Такие моменты часто имеют значение, например, при анализе транзакций, чтобы получить список n продуктов с самыми высокими продажами. Или мы, анализируя активность на itresume.ru при отборе страниц с наибольшим количеством посещений, не можем ограничиться head() - нам важны все значения (даже повторяющиеся).

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

df.nlargest(n=3,
columns='Оценка',
keep='all')

| | Имя | Оценка |
|---|-------|--------|
| 1 | Яна | 100 |
| 2 | Петр | 97 |
| 0 | Игорь | 95 |
| 3 | Иван | 95 |

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

‼️ В нашем бесплатном курсе по Python рассказываем, как работать с модулями и библиотеками Python: как устанавливать, импортировать и какие могут быть подводные камни ☺️

#python #pandas
🔥22👍13
🔥 Создание поля объектов в 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