Как стать автором
Обновить

Preprocessing pandas dataframes. Предварительная обработка данных в пандас датафреймах

Уровень сложностиПростой
Время на прочтение22 мин
Количество просмотров1.6K

Обработка датафреймов: ключевые аспекты и инструменты

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

Что такое датафрейм?

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

Основные операции с датафреймами

  1. Загрузка данных
    Датафреймы можно создавать из различных источников: CSV-файлов, баз данных, Excel-таблиц или API.

  2. Просмотр данных
    Для ознакомления с данными используются методы:

    • head() — вывод первых строк.

    • tail() — вывод последних строк.

    • info() — информация о структуре данных.

    • describe() — статистика по числовым столбцам.

# проверим данные на присутвие дубликатов
lst = [df1, df2, df3, df4] # инициализируем список со всеми таблицами
sl = {} # инициализируем словарь для записи результатов

# проходимся циклом по всем таблицам и проверяем наличие дубликатов
for i, df in tqdm(enumerate(lst), total = len(lst)):
    sl[f'df{i+1}'] = df.duplicated().sum()

# выводим словарь с результатами проверки
sl

Фильтрация и выбор данных

Фильтрация позволяет выбирать данные по условиям.

Агрегация данных

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

Обработка пропущенных значений

Пропущенные значения можно удалить или заполнить.

# проверим данные на присутвие пропусков
lst = [df1, df2, df3, df4] # инициализируем список со всеми таблицами
sl = {} # инициализируем словарь для записи результатов

# проходимся циклом по всем таблицам и проверяем наличие пропусков
for i, df in tqdm(enumerate(lst), total = len(lst)):
    sl[f'df{i+1}'] = df.isna().sum().sum()

# выводим словарь с результатами проверки
sl

# удаление некорректных колонок
df4.dropna(axis = 1, thresh = len(df) - 10000, inplace = True)
df4

Преобразование данныхДатафреймы поддерживают множество операций для преобразования данных, например:

# инициализация функции приведение к числовому формату 
def clear_df1(val):
    return str(val).split('(')[0]

# приведение к числовому формату
df1['Общее количество достопримечательностей'] = df1['Общее количество достопримечательностей'].progress_apply(lambda x: int(clear_df1(x)))
df1.info()

Сортировка данных

Данные можно сортировать по одному или нескольким столбцам.

Популярные библиотеки для работы с датафреймами

  • Pandas (Python) — самая популярная библиотека для работы с датафреймами. Она предоставляет мощные инструменты для обработки и анализа данных.

  • Dplyr (R) — библиотека для работы с данными в языке R, известная своим удобным синтаксисом.

  • Spark DataFrame (Scala/Python/Java) — инструмент для распределённой обработки больших объёмов данных.

  • Polars (Rust/Python) — быстрая альтернатива Pandas для работы с большими датасетами.

Заключение

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


import pandas as pd
import numpy as np
from tqdm.notebook import tqdm
import torch
import re
import nltk

nltk.download('stopwords')
from nltk.corpus import stopwords
from sklearn.model_selection import train_test_split
[nltk_data] Downloading package stopwords to
[nltk_data]     C:\Users\ffedo\AppData\Roaming\nltk_data...
[nltk_data]   Package stopwords is already up-to-date!
torch.cuda.is_available()
True
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device
device(type='cuda')
df = pd.read_csv('train.csv')
df.drop(columns=['index'], inplace=True)
df
review	sentiment
0	Есть много причин, по которым 'Война и мир' Ль...	1
1	Напишите 5 предложений. 1. "Война и мир" — это...	2
2	[Практикуйте «Другие люди»] Отзыв о «Войне и ...	1
3	Стремитесь к точному и объективному представле...	2
4	В книге "Война и мир" Льва Толстого разворачив...	2
...	...	...
23995	Вот и все, что нужно сделать: Во-первых, объяс...	1
23996	Обязательно укажите, что именно в тексте/сюжет...	0
23997	Кто, из героев вас заинтересовал и почему? Как...	1
23998	Жду историй о том, как книга изменила ваше вос...	1
23999	Вот несколько причин, почему «Война и мир» Льв...	1
24000 rows × 2 columns

sw = stopwords.words('russian')

def clean_text(text):
    
    text = text.lower()
    
    text = re.sub(r'\W+', ' ', text) # replacing everything with space except (a-z, A-Z, ".", "?", "!", ",")

    text = re.sub(r"http\S+", "",text) #Removing URLs 
    #text = re.sub(r"http", "",text)
    
    html=re.compile(r'<.*?>') 
    
    text = html.sub(r'',text) #Removing html tags
    
    punctuations = '@#!?+&*[]-%.:/();$=><|{}^' + "'`" + '_'
    for p in punctuations:
        text = text.replace(p,'') #Removing punctuations
        
    text = [word.lower() for word in text.split() if word.lower() not in sw]
    
    text = " ".join(text) #removing stopwords

    emoji_pattern = re.compile("["
                           u"\U0001F600-\U0001F64F"  # emoticons
                           u"\U0001F300-\U0001F5FF"  # symbols & pictographs
                           u"\U0001F680-\U0001F6FF"  # transport & map symbols
                           u"\U0001F1E0-\U0001F1FF"  # flags (iOS)
                           u"\U00002702-\U000027B0"
                           u"\U000024C2-\U0001F251"
                           "]+", flags=re.UNICODE)
    text = emoji_pattern.sub(r'', text) #Removing emojis
    
    return text
df['review'] = df['review'].apply(lambda x: clean_text(x))
df
review	sentiment
0	причин которым война мир льва толстого считает...	1
1	напишите 5 предложений 1 война мир это роман л...	2
2	практикуйте другие люди отзыв войне мире льва ...	1
3	стремитесь точному объективному представлению ...	2
4	книге война мир льва толстого разворачивается ...	2
...	...	...
23995	нужно сделать первых объясните заключается кни...	1
23996	обязательно укажите именно тексте сюжете персо...	0
23997	героев заинтересовал почему какова судьба толс...	1
23998	жду историй книга изменила ваше восприятие мир...	1
23999	несколько причин почему война мир льва толстог...	1
24000 rows × 2 columns

from transformers import BertTokenizer
tokenizer_path = 'cointegrated/rubert-tiny'
tokenizer = BertTokenizer.from_pretrained(tokenizer_path, do_lower_case=True)
c:\Users\ffedo\AppData\Local\Programs\Python\Python311\Lib\site-packages\huggingface_hub\file_download.py:797: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
  warnings.warn(
max_len = 0
for sent in tqdm(df.review):

    # Tokenize the text and add `[CLS]` and `[SEP]` tokens.
    input_ids = tokenizer.encode(sent, truncation=True, add_special_tokens=True)

    # Update the maximum sentence length.
    max_len = max(max_len, len(input_ids))

print('Max sentence length: ', max_len)
  0%|          | 0/24000 [00:00<?, ?it/s]
Max sentence length:  512
from torch.utils.data import Dataset

class CustomDataset(Dataset):

  def __init__(self, texts, targets, tokenizer, max_len=512):
    self.texts = texts
    self.targets = targets
    self.tokenizer = tokenizer
    self.max_len = max_len

  def __len__(self):
    return len(self.texts)

  def __getitem__(self, idx):
    text = str(self.texts[idx])
    target = self.targets[idx]

    encoding = self.tokenizer.encode_plus(
        text,
        add_special_tokens=True,
        max_length=self.max_len,
        return_token_type_ids=False,
        truncation=True,
        padding='max_length',
        # pad_to_max_length = True,
        return_attention_mask=True,
        return_tensors='pt',
    )

    return {
      'text': text,
      'input_ids': encoding['input_ids'].flatten(),
      'attention_mask': encoding['attention_mask'].flatten(),
      'targets': torch.tensor(target, dtype=torch.long)
    }
# input_ids = []
# attention_masks = []
# labels = df.sentiment.values

# # For every tweet...
# for tweet in tqdm(df.review):
#     # `encode_plus` will:
#     #   (1) Tokenize the sentence.
#     #   (2) Prepend the `[CLS]` token to the start.
#     #   (3) Append the `[SEP]` token to the end.
#     #   (4) Map tokens to their IDs.
#     #   (5) Pad or truncate the sentence to `max_length`
#     #   (6) Create attention masks for [PAD] tokens.
#     encoded_dict = tokenizer.encode_plus(
#                         tweet,                      # Sentence to encode.
#                         add_special_tokens = True, # Add '[CLS]' and '[SEP]'
#                         max_length = max_len,   
#                         padding=True,
#                         truncation=True,
#                         # pad_to_max_length = True,
#                         return_attention_mask = True,   # Construct attn. masks.
#                         return_tensors = 'pt',     # Return pytorch tensors.
#                    )
    
#     # Add the encoded sentence to the list.    
#     input_ids.append(encoded_dict['input_ids'])

#     # And its attention mask (simply differentiates padding from non-padding).
#     attention_masks.append(encoded_dict['attention_mask'])

# # Convert the lists into tensors.
# input_ids = torch.cat(input_ids, dim=0)
# attention_masks = torch.cat(attention_masks, dim=0)
# labels = torch.tensor(labels)
from transformers import BertForSequenceClassification
model_path = 'cointegrated/rubert-tiny'
model = BertForSequenceClassification.from_pretrained(model_path)
c:\Users\ffedo\AppData\Local\Programs\Python\Python311\Lib\site-packages\huggingface_hub\file_download.py:797: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
  warnings.warn(
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at cointegrated/rubert-tiny and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
out_features = model.bert.encoder.layer[1].output.dense.out_features
out_features
312
model
BertForSequenceClassification(
  (bert): BertModel(
    (embeddings): BertEmbeddings(
      (word_embeddings): Embedding(29564, 312, padding_idx=0)
      (position_embeddings): Embedding(512, 312)
      (token_type_embeddings): Embedding(2, 312)
      (LayerNorm): LayerNorm((312,), eps=1e-12, elementwise_affine=True)
      (dropout): Dropout(p=0.1, inplace=False)
    )
    (encoder): BertEncoder(
      (layer): ModuleList(
        (0-2): 3 x BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=312, out_features=312, bias=True)
              (key): Linear(in_features=312, out_features=312, bias=True)
              (value): Linear(in_features=312, out_features=312, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=312, out_features=312, bias=True)
              (LayerNorm): LayerNorm((312,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=312, out_features=600, bias=True)
            (intermediate_act_fn): GELUActivation()
          )
          (output): BertOutput(
            (dense): Linear(in_features=600, out_features=312, bias=True)
            (LayerNorm): LayerNorm((312,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
      )
    )
    (pooler): BertPooler(
      (dense): Linear(in_features=312, out_features=312, bias=True)
      (activation): Tanh()
    )
  )
  (dropout): Dropout(p=0.1, inplace=False)
  (classifier): Linear(in_features=312, out_features=2, bias=True)
)
model.classifier = torch.nn.Linear(312, 3)
model
BertForSequenceClassification(
  (bert): BertModel(
    (embeddings): BertEmbeddings(
      (word_embeddings): Embedding(29564, 312, padding_idx=0)
      (position_embeddings): Embedding(512, 312)
      (token_type_embeddings): Embedding(2, 312)
      (LayerNorm): LayerNorm((312,), eps=1e-12, elementwise_affine=True)
      (dropout): Dropout(p=0.1, inplace=False)
    )
    (encoder): BertEncoder(
      (layer): ModuleList(
        (0-2): 3 x BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=312, out_features=312, bias=True)
              (key): Linear(in_features=312, out_features=312, bias=True)
              (value): Linear(in_features=312, out_features=312, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=312, out_features=312, bias=True)
              (LayerNorm): LayerNorm((312,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=312, out_features=600, bias=True)
            (intermediate_act_fn): GELUActivation()
          )
          (output): BertOutput(
            (dense): Linear(in_features=600, out_features=312, bias=True)
            (LayerNorm): LayerNorm((312,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
      )
    )
    (pooler): BertPooler(
      (dense): Linear(in_features=312, out_features=312, bias=True)
      (activation): Tanh()
    )
  )
  (dropout): Dropout(p=0.1, inplace=False)
  (classifier): Linear(in_features=312, out_features=3, bias=True)
)
X = df.review
X
0        причин которым война мир льва толстого считает...
1        напишите 5 предложений 1 война мир это роман л...
2        практикуйте другие люди отзыв войне мире льва ...
3        стремитесь точному объективному представлению ...
4        книге война мир льва толстого разворачивается ...
                               ...                        
23995    нужно сделать первых объясните заключается кни...
23996    обязательно укажите именно тексте сюжете персо...
23997    героев заинтересовал почему какова судьба толс...
23998    жду историй книга изменила ваше восприятие мир...
23999    несколько причин почему война мир льва толстог...
Name: review, Length: 24000, dtype: object
y = df.sentiment
y
0        1
1        2
2        1
3        2
4        2
        ..
23995    1
23996    0
23997    1
23998    1
23999    1
Name: sentiment, Length: 24000, dtype: int64
# from torch.utils.data import TensorDataset, random_split
# dataset = TensorDataset(input_ids, attention_masks, labels)

# # Create a 90-10 train-validation split.

# # Calculate the number of samples to include in each set.
# train_size = int(0.9 * len(dataset))
# #val_size = int(0.2 * len(dataset))
# val_size = len(dataset)  - train_size

# # Divide the dataset by randomly selecting samples.
# train_set, valid_set = random_split(dataset, [train_size, val_size])

# print('{:>5,} training samples'.format(train_size))
# print('{:>5,} validation samples'.format(val_size))
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.1, random_state=1001, shuffle=False)
from torch.utils.data import DataLoader
train_set = CustomDataset(X_train, y_train, tokenizer)

train_loader = DataLoader(train_set, batch_size=16, shuffle=True)
valid_set = CustomDataset(X_train, y_train, tokenizer)
valid_loader = DataLoader(valid_set, batch_size=16, shuffle=False)
train_set[6049]
{'text': 'забудьте указать ваше мнение содержании качество языка структуру книги изобразительные средства использованные автором другие моменты которые показались неубедительными отзыв книгу война мир льва толстого анализ слабых сторон лев толстой безусловно величайших русских писателей война мир это колоссальный труд однако несмотря признание восторги критиков книга имеет несколько слабых сторон которые взгляд заслуживают обсуждения содержание одна главных проблем войны мира это избыточность запутанность сюжета толстой стремился охватить множество персонажей включая исторические события философские размышления драматические любовные истории это приводит тому некоторые сюжетные линии теряют значимость некоторых частях книги возникает ощущение ненужной затянутости например эпизоды посвящённые наполеону порой отходят главных героев оставляя читателя недоумении соотносятся основной темой качество языка хотя стиль толстого часто восхваляется богатство глубину войне мире язык становится чрезмерно сложным напыщенным длинные описания философские отступления могут отвлекать основной сюжетной линии утомлять читателя отрывки напоминают столько художественное произведение сколько эссе исследовательскую работу структура книги структура войны мира неравн',
 'input_ids': tensor([    2,   650, 25626,   988, 16296, 11048,  1348,  2085,  2594, 25236,
          1154,  6834, 10201,  1768, 17465, 10955,  8398, 24362,  6661,   778,
         10658, 11106, 25380, 11201, 12029, 12789, 18274,  1308, 17409,  5685,
          6024,   700,  2938, 26422,  1725,   769, 12875, 24274, 20963,   613,
           733,  5892,   887, 15655,   815,  2462,  9311,   323, 28632,  1619,
         17220,  8761, 20578, 16410, 15984,  2313, 25381, 26335,   887,  1619,
         17220,  1026,   613,  2399,  5729,  5021,   948, 22339,  2411, 18231,
          2395, 14751, 19940, 11787,   815,  2462,  9311,  2389, 12313, 15343,
         21611,   613, 19517,  4580, 15096, 29455,  2215,   815, 19445,  2987,
         29521,   811,  8365,  5287,  3910, 16410, 15984,  2313, 25381,  2938,
           314,  1556, 13386,   650, 24741, 23417,  2514,  4108, 12815,  1154,
         22523,  6664, 19435, 11456,   815, 11137,  3588,  2389,   778,  5206,
         26989,  2242,   650, 11357,  3460,  4420, 26318,   603,  1619, 17220,
          1026,   613,  5406, 14410,  4465,   326,   753,  5462,  1348, 10192,
         24018, 11787, 11030, 27987,   626, 21656,   332, 28422,  3095,  4495,
          3754,  1276,  5140, 15095, 19866, 27993,  1308,  3869,  2389, 21819,
          3567,  9831, 26318,  1308,  7861,  2710, 12550,  2763, 18385, 28734,
          8904,  4916,  6917,  6661,   815, 20492, 17585,   326, 15910,  8397,
           769,  1673,  7673,   613,   650, 10272, 22139,  8495,  5767, 20449,
           700,   705,   866, 24155, 15764,  1308,   548, 16615,  1736, 11050,
         13276,  8461,   733, 19292, 19435, 28454, 24485, 11835, 25549,  4781,
         18773,  8199,   769,  4823, 24460,  4483,  1154,  1186, 22973, 14422,
         26241,   613,  5624,  8461, 17465, 10955,  8398,  8285, 19354,  1619,
         17220,  8761,  5166,   815,   866,   753,  5296, 13773, 28288, 21735,
         17189, 12875,  9062,   815,  5980, 10348,  9441, 11566, 12415, 10885,
           948, 16410, 27446,   548,  8086, 15764,  1635,   316,  5952,  1308,
           326, 19753,   332, 28422,  3095, 28067, 29253,  5140,  6716,   733,
           887,  9573,  4963, 26241,   613, 26318,   948,   613,  7861,   331,
          5269, 10429,  4781, 18773,  8199,   733, 22791,   954,   548, 25625,
          2462,  2763, 29125, 21605, 27212, 26366, 17725, 28342,   329, 27672,
           341, 21917,   320, 10786,  1736, 12439,  5390,  3916,  8471,  8898,
          6661,  8898,   815, 11137,  3588,   769, 13266,   679,     3,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0]),
 'attention_mask': tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0]),
 'targets': tensor(0)}
from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=2e-5, correct_bias=False)
c:\Users\ffedo\AppData\Local\Programs\Python\Python311\Lib\site-packages\transformers\optimization.py:521: FutureWarning: This implementation of AdamW is deprecated and will be removed in a future version. Use the PyTorch implementation torch.optim.AdamW instead, or set `no_deprecation_warning=True` to disable this warning
  warnings.warn(
epochs =10
from transformers import get_linear_schedule_with_warmup
scheduler = get_linear_schedule_with_warmup(
                optimizer,
                num_warmup_steps=0,
                num_training_steps=len(train_loader) * epochs
            )
loss_fn = torch.nn.CrossEntropyLoss()
def fit(model, train_loader, device, loss_fn, optimizer, scheduler, train_set):
    model = model.train()
    losses = []
    correct_predictions = 0

    for data in tqdm(train_loader):
        input_ids = data["input_ids"].to(device)
        attention_mask = data["attention_mask"].to(device)
        targets = data["targets"].to(device)

        outputs = model(
            input_ids=input_ids,
            attention_mask=attention_mask
            )

        preds = torch.argmax(outputs.logits, dim=1)
        loss = loss_fn(outputs.logits, targets)

        correct_predictions += torch.sum(preds == targets)

        losses.append(loss.item())
        
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        optimizer.step()
        scheduler.step()
        optimizer.zero_grad()

    train_acc = correct_predictions.double() / len(train_set)
    train_loss = np.mean(losses)
    return train_acc, train_loss
def eval(model, valid_loader, device, loss_fn, valid_set):
    model = model.eval()
    losses = []
    correct_predictions = 0

    with torch.no_grad():
        for data in tqdm(valid_loader):
            input_ids = data["input_ids"].to(device)
            attention_mask = data["attention_mask"].to(device)
            targets = data["targets"].to(device)

            outputs = model(
                input_ids=input_ids,
                attention_mask=attention_mask
                )

            preds = torch.argmax(outputs.logits, dim=1)
            loss = loss_fn(outputs.logits, targets)
            correct_predictions += torch.sum(preds == targets)
            losses.append(loss.item())
    
    val_acc = correct_predictions.double() / len(valid_set)
    val_loss = np.mean(losses)
    return val_acc, val_loss
model.to(device)
best_accuracy = 0
for epoch in range(epochs):
    print(f'Epoch {epoch + 1}/{epochs}')
    train_acc, train_loss = fit(model, train_loader, device, loss_fn, optimizer, scheduler, train_set)
    print(f'Train loss {train_loss} accuracy {train_acc}')

    val_acc, val_loss = eval(model, valid_loader, device, loss_fn, valid_set)
    print(f'Val loss {val_loss} accuracy {val_acc}')
    print('-' * 10)

    # if val_acc > best_accuracy:
    #     torch.save(model, model_save_path)
    #     best_accuracy = val_acc

# model = torch.load(model_save_path)
Epoch 1/10
  0%|          | 0/1350 [00:00<?, ?it/s]
Train loss 0.25926221240139397 accuracy 0.9030555555555555
  0%|          | 0/1350 [00:00<?, ?it/s]
Val loss 0.19813466539968633 accuracy 0.9220833333333333
----------
Epoch 2/10
  0%|          | 0/1350 [00:00<?, ?it/s]
# imports
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from tqdm.notebook import tqdm
import phik 
from phik.report import plot_correlation_matrix
from collections import Counter
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import f1_score, accuracy_score, make_scorer
import optuna
from catboost import CatBoostClassifier
from sklearn.ensemble import VotingClassifier
import torch
from torch import nn
from torch.utils.data import DataLoader, Dataset
from torchvision import models as vision_models
import torch.optim as optim
import torchvision
import torchvision.transforms as tt
from PIL import Image
import random
from pathlib import Path
import os
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA

tqdm.pandas()

# Зафиксируем сиды, чтобы обучение было воспроизводимым.
def set_seed(seed):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)

    if torch.cuda.is_available():
        torch.cuda.manual_seed(seed)
        torch.cuda.manual_seed_all(seed)

    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

set_seed(1001)

import warnings
warnings.filterwarnings("ignore")
# data loading
df = pd.read_csv('train.csv')
df.head(3)
df.info()
pred_df = pd.read_csv('test.csv')
pred_df.head(3)
pred_df.info()
# num_rays
Image.open('images/'+df['drawing'][0]).convert("RGB")
import cv2
import numpy as np

def count_rays(image_path):
    """
    Определяет количество лучей у снежинки на изображении.

    Args:
        image_path: Путь к изображению снежинки.

    Returns:
        Количество лучей (int) или None, если не удалось определить.
    """
    try:
        # Загружаем изображение в оттенках серого
        img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

        # Применяем пороговое преобразование, чтобы отделить снежинку от фона
        _, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

        # Находим контуры на изображении
        contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # Находим самый большой контур (предполагаем, что это снежинка)
        if not contours:
            print("Контуры не найдены.")
            return None
        cnt = max(contours, key=cv2.contourArea)

        # Аппроксимируем контур многоугольником
        epsilon = 0.01 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, epsilon, True)

        # Количество вершин многоугольника примерно равно количеству лучей
        # (может потребоваться корректировка в зависимости от формы снежинки)
        # В данном случае, учитывая форму представленной снежинки,
        # количество лучей равно количеству вершин, деленному на 2, так как каждая вершина - это либо начало луча, либо конец разветвления.
        # Если бы разветвлений не было, то количество лучей было бы равно количеству вершин.
        num_rays = len(approx) // 2

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

        return num_rays

    except Exception as e:
        print(f"Произошла ошибка: {e}")
        return None

# Пример использования
image_path = 'images/'+df['drawing'][0]  # Замените на путь к вашему изображению
num_rays = count_rays(image_path)

if num_rays is not None:
    print(f"Количество лучей: {num_rays}")
lst=[]

for i in tqdm(range(len(df))):
    if str(df['drawing'][i]) != 'nan':
        num_rays = count_rays('images/'+df['drawing'][i])
        lst.append(num_rays)
    else: lst.append(np.nan)
    
df['num_rays'] = lst
df
# image clustering
img_df = df[['drawing']]
img_df.dropna(inplace=True)
img_df
image_path = 'images'

# Создание новой колонки 'images' с numpy-массивами изображений
def read_image(filename):
    try:
        img = Image.open(os.path.join(image_path, filename))
        # Преобразование изображения в RGB (если оно не в RGB) и в numpy-массив
        img = img.convert('RGB')
        return np.array(img)
    except Exception as e:
        print(f"Ошибка при чтении файла {filename}: {e}")
        return None
img_df['images'] = img_df['drawing'].progress_apply(read_image)
pca = PCA(n_components=100)
images = np.array([img.flatten() for img in tqdm(img_df['images'])])
# images = pca.fit_transform(np.array([img.flatten() for img in img_df['images']]))
kmeans = KMeans(n_clusters=5, random_state=1001)
kmeans.fit(images)
img_df['cluster'] = kmeans.labels_
img_df.drop(columns=['images'], inplace=True)
img_df
Counter(img_df.cluster)
df = pd.merge(df, img_df, on='drawing', how='left')
df
del img_df
# data preprocessing
len(df.columns)
# feature engineering
df['new_let'] = df['letter'].notnull().astype(int) 

# df['new_com'] = df['comments'].notnull().astype(int) херня

age_cluster = KMeans(n_clusters=3, random_state=1001)
df['age_cluster'] = age_cluster.fit_predict(np.array(df['age_num']).reshape(-1, 1))

df
df.drop(columns=['drawing'], inplace=True)
df['letter'].fillna('', inplace=True)
# df['comments'].fillna('', inplace=True)
df.isna().sum()
len(df.columns)
# визуализация распределения признаков
def visualize_distributions(df, numeric_only=True):
    """
    Визуализирует распределение признаков в DataFrame.

    Args:
        df (pd.DataFrame): DataFrame с данными.
        numeric_only (bool, optional): Визуализировать только числовые признаки. По умолчанию True.
    """

    if numeric_only:
        numeric_cols = df.select_dtypes(include=['number']).columns
    else:
        numeric_cols = df.columns
    
    if len(numeric_cols) == 0:
      print("Нет числовых признаков для визуализации.")
      return
    
    num_cols = len(numeric_cols)
    num_rows = (num_cols + 2) // 3  # Определяем количество строк для подграфиков

    plt.figure(figsize=(15, 5 * num_rows)) # Делаем размеры рисунка побольше

    for i, column in enumerate(numeric_cols, 1):
        plt.subplot(num_rows, 3, i) # Создаем подграфик на каждой итерации
        sns.histplot(df[column], kde=True)  # Гистограмма с KDE (оценка плотности ядра)
        plt.title(f'Распределение {column}') # Выводим заголовок к графику
        plt.xlabel(column) # Выводим название оси Х
        plt.ylabel("Количество") # Выводим название оси Y

    plt.tight_layout()  # Автоматически регулирует расположение графиков
    plt.show()


# Визуализируем распределения числовых признаков
visualize_distributions(df.drop(columns=['gift_receiving']))
![image.png](attachment:image.png)
df.drop(columns=['sex', 'activity_level', 'skiped_lessons', 'good_deeds'], inplace=True)
df.head()
le_cols = ['school_results', 'lazy_level']
from sklearn.preprocessing import LabelEncoder

le_school_results = LabelEncoder()
le_lazy_level = LabelEncoder()

for i in tqdm(le_cols):
    if i == 'school_results': df[i] = le_school_results.fit_transform(df[i])
    if i == 'lazy_level': df[i] = le_lazy_level.fit_transform(df[i])
phik_matrix = df.drop(columns=['person']).phik_matrix()

plot_correlation_matrix(phik_matrix.values,
                        x_labels = phik_matrix.columns,
                        y_labels = phik_matrix.index,
                        title = 'phik correlation matrix',
                        fontsize_factor=0.8, figsize=(11, 6)
)
df.drop(columns=['comments', 'lied'], inplace=True)
# create training data
X = df.drop(columns = ['gift_receiving', 'person'])
X
y = df.gift_receiving
y
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.1, random_state=1001, stratify=y)
# optuna + catboost
# # Функция для оценки модели с заданными гиперпараметрами
# def objective(trial):
#     # Определение пространства поиска гиперпараметров
#     # params = {
#     #     'iterations': trial.suggest_int('iterations', 100, 3000),
#     #     'learning_rate': trial.suggest_loguniform('learning_rate', 0.01, 0.3),
#     #     'depth': trial.suggest_int('depth', 2, 10),
#     #     'l2_leaf_reg': trial.suggest_loguniform('l2_leaf_reg', 1e-8, 100.0),
#     #     'border_count': trial.suggest_int('border_count', 32, 255),
#     #     # 'random_strength': trial.suggest_uniform('random_strength', 0.5, 50),
#     #     # 'bagging_temperature': trial.suggest_loguniform('bagging_temperature', 0.01, 10.0),
#     #     # 'grow_policy': trial.suggest_categorical('grow_policy', ['SymmetricTree', 'Depthwise', 'Lossguide']),
#     #     # 'min_data_in_leaf': trial.suggest_int('min_data_in_leaf', 1, 50),
#     #     # 'max_leaves': trial.suggest_int('max_leaves', 2, 64) if params['grow_policy'] == 'Lossguide' else None,
#     #     # 'bootstrap_type': trial.suggest_categorical('bootstrap_type', ['Bayesian', 'Bernoulli', 'MVS']),
#     #     'task_type': 'CPU',  # Или 'GPU', если вы хотите использовать GPU
#     #     'verbose': 0,
#     #     'random_seed': 1001
#     # }

#     params = {
#         "iterations": trial.suggest_int('iterations', 100, 3000),
#         # "learning_rate": trial.suggest_float("learning_rate", 1e-3, 0.1, log=True),
#         'learning_rate': trial.suggest_loguniform('learning_rate', 0.01, 0.3),
#         'text_features':['letter', 'comments'],
#         "depth": trial.suggest_int("depth", 1, 10),
#         'l2_leaf_reg': trial.suggest_loguniform('l2_leaf_reg', 1e-8, 100.0),
#         "subsample": trial.suggest_float("subsample", 0.05, 1.0),
#         "colsample_bylevel": trial.suggest_float("colsample_bylevel", 0.05, 1.0),
#         "min_data_in_leaf": trial.suggest_int("min_data_in_leaf", 1, 100),
#         'task_type': 'CPU',  # Или 'GPU', если вы хотите использовать GPU
#         'verbose': 0,
#         'random_seed': 1001
#     }

#     # Создание и обучение модели
#     model = CatBoostClassifier(**params)
#     model.fit(X_train, y_train)
    
#     # Определите макро F1-score как scorer
#     # macro_f1_scorer = make_scorer(f1_score, average='macro')

#     # Используйте cross_val_score с макро F1-score
#     # scores = cross_val_score(model, X_train, y_train, cv=5, scoring=macro_f1_scorer)
#     scores = f1_score(y_test, model.predict(X_test), average='macro')
    
#     # Возвращаем среднее значение метрики (accuracy) для кросс-валидации
#     return scores
#     # return scores.mean()
# # Создание объекта исследования
# study = optuna.create_study(direction='maximize')
# # Запуск оптимизации
# study.optimize(objective, n_trials=3)
# df = study.trials_dataframe()
# best_trials_df = df.sort_values('value', ascending=False).head(3)
# best_trials_df
# best_trials = sorted(study.trials, key=lambda trial: trial.value, reverse=True)[:3]
# best_trials
# X_train['rand'] = np.random.rand(7200)
# X_test['rand'] = np.random.rand(800)
model = CatBoostClassifier(# text_features=['letter', 'comments'],
                            text_features=['letter'],
                            task_type='CPU',
                            random_seed=1001,  
                            verbose=52)
model.fit(X_train, y_train)
feature_importance = model.feature_importances_
sorted_idx = np.argsort(feature_importance)
fig = plt.figure(figsize=(15, 10))
plt.barh(range(len(sorted_idx)), feature_importance[sorted_idx], align='center')
plt.yticks(range(len(sorted_idx)), np.array(X_test.columns)[sorted_idx])
plt.title('Feature Importance')
# best_params = study.best_params
# best_model = CatBoostClassifier(**best_params,
#                             text_features=['letter', 'comments'],
#                             task_type='CPU',
#                             random_seed=1001,  
#                             verbose=52)
# best_model.fit(X_train, y_train)
predictions = model.predict(X_test)
f1_score(y_test, predictions, average='macro')
# predict
pred_df = pd.read_csv('test.csv')
pred_df.head(3)
img_df = pred_df[['drawing']]
img_df.dropna(inplace=True)
img_df
img_df['images'] = img_df['drawing'].progress_apply(read_image)
images = np.array([img.flatten() for img in tqdm(img_df['images'])])
# images = pca.transform(np.array([img.flatten() for img in img_df['images']]))
kmeans.predict(images)
img_df['cluster'] = kmeans.predict(images)
img_df.drop(columns=['images'], inplace=True)
img_df
Counter(img_df['cluster'])
pred_df = pd.merge(pred_df, img_df, on='drawing', how='left')
pred_df
pred_df['new_let'] = pred_df['letter'].notnull().astype(int)
# pred_df['new_com'] = pred_df['comments'].notnull().astype(int)
pred_df
pred_df.drop(columns=['drawing'], inplace=True)
pred_df.fillna('', inplace=True)
pred_df.isna().sum()
pred_df.drop(columns=['person', 'sex', 'activity_level', 'skiped_lessons', 'good_deeds'], inplace=True)
pred_df.drop(columns=['comments'], inplace=True)
for i in tqdm(le_cols):
    if i == 'school_results': pred_df[i] = le_school_results.fit_transform(pred_df[i])
    if i == 'lazy_level': pred_df[i] = le_lazy_level.fit_transform(pred_df[i])
pred_df.fillna('', inplace=True)
sample = pd.read_csv('sample_submission.csv')
sample
# pred_df['rand'] = np.random.rand(len(pred_df))
# predictions = ensemble_model_voting.predict(pred_df)
predictions = model.predict(pred_df)
sample['gift_receiving'] = predictions
Counter(sample['gift_receiving'])
sample.to_csv('sub.csv', index=False)
import pandas as pd
import numpy as np
import phik 
from phik.report import plot_correlation_matrix
from sklearn.preprocessing import LabelEncoder
import random
import torch
from tqdm.notebook import tqdm
from lightautoml.automl.presets.tabular_presets import TabularAutoML
from lightautoml.tasks import Task
from sklearn.metrics import mean_absolute_percentage_error as mape

def set_seed(seed):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)

    if torch.cuda.is_available():
        torch.cuda.manual_seed(seed)
        torch.cuda.manual_seed_all(seed)

    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

set_seed(1001)

import warnings
warnings.filterwarnings("ignore")
df = pd.read_csv('train.csv', index_col=0)
df
pred_df = pd.read_csv('test.csv', index_col = 0)
pred_df
print(df['price'].max(), df['price'].min())
df['price'] = df['price'].apply(lambda x: np.log10(x + 1))
df
# df['floor_coef'] = df['number_of_floors'] / df['floor']
# pred_df['floor_coef'] = pred_df['number_of_floors'] / pred_df['floor']

# df['not_living'] = df['area'] - df['living_area']
# pred_df['not_living'] = pred_df['area'] - pred_df['living_area']

# df['living_kitchen'] = df['kitchen_area'] + df['living_area']
# pred_df['living_kitchen'] = pred_df['kitchen_area'] + pred_df['living_area']

# df.head(2)
np.unique(df.region)
df.info()
cat_cols = ['apartment_type', 'metro_station', 'region', 'renovation']
le = LabelEncoder()
for column in tqdm(cat_cols):
    df[column] = le.fit_transform(df[column])
    pred_df[column] = le.transform(pred_df[column])
phik_matrix = df.phik_matrix()

plot_correlation_matrix(phik_matrix.values,
                        x_labels = phik_matrix.columns,
                        y_labels = phik_matrix.index,
                        title = 'phik correlation matrix',
                        fontsize_factor=0.8, figsize=(11, 6)
)
automl = TabularAutoML(
    task = Task(
        name = 'reg',
        loss = 'mae',
        metric = lambda y_true, y_pred: 100 / (1 + mape(y_true, y_pred)))
)
oof_pred = automl.fit_predict(
    df,
    roles = {'target': 'price', 'drop': []}
)
test_pred = automl.predict(pred_df)
test_pred
test_pred = np.power(10, test_pred.data[:, 0]) - 1
test_pred
sample = pd.read_csv('test.csv')['index']
sample
df1 = pd.DataFrame({
    'index':sample.index,
    'price': test_pred
}).to_csv('submit.csv', index = False)

Теги:
Хабы:
Всего голосов 4: ↑1 и ↓3-2
Комментарии1

Публикации

Ближайшие события