Что такое Lorem Ipsum?
Lorem Ipsum — это просто пустой текст, используемый в полиграфической и наборной индустрии. Lorem Ipsum был стандартным текстом-пустышкой в отрасли с 1500-х годов, когда неизвестный типограф взял гранку шрифтов и переделал ее, чтобы сделать книгу образцов шрифтов. Он пережил не только пять столетий, но и скачок в электронную верстку, оставшись по существу неизменным. Он был популяризирован в 1960-х годах с выпуском листов Letraset, содержащих отрывки из Lorem Ipsum, а в последнее время с появлением программного обеспечения для настольных издательских систем, такого как Aldus PageMaker, включая версии Lorem Ipsum.
Почему мы его используем?
Это давно установленный факт, что читатель будет отвлекаться на читаемое содержимое страницы, когда смотрит на ее макет. Смысл использования Lorem Ipsum в том, что он имеет более или менее нормальное распределение букв, в отличие от использования «Содержимое здесь, содержимое здесь», что делает его похожим на читаемый английский язык. Многие пакеты настольных издательских систем и редакторы веб-страниц теперь используют Lorem Ipsum в качестве текста модели по умолчанию, а поиск по запросу «lorem ipsum» позволит обнаружить многие веб-сайты, которые все еще находятся в зачаточном состоянии. На протяжении многих лет развивались различные версии, иногда случайно, иногда намеренно (привнесенный юмор и тому подобное).
Откуда это взялось?
Вопреки распространенному мнению, Lorem Ipsum — это не просто случайный текст. Он уходит корнями в произведение классической латинской литературы 45 г. до н.э., то есть ему более 2000 лет. Ричард МакКлинток, профессор латыни в Хэмпден-Сиднейском колледже в Вирджинии, нашел одно из наиболее малоизвестных латинских слов, consectetur, в отрывке из Lorem Ipsum и, просматривая цитаты этого слова в классической литературе, обнаружил несомненный источник. Lorem Ipsum взят из разделов 1.10.32 и 1.10.33 книги Цицерона «de Finibus Bonorum et Malorum» («Крайности добра и зла»), написанной в 45 г. до н.э. Эта книга представляет собой трактат по теории этики, очень популярный в эпоху Возрождения. Первая строка Lorem Ipsum, «Lorem ipsum dolor sit amet..», взята из строки из раздела 1.10.32.
Стандартный фрагмент Lorem Ipsum, используемый с 1500-х годов, для интересующихся воспроизведен ниже. Разделы 1.10.32 и 1.10.33 из «de Finibus Bonorum et Malorum» Цицерона также воспроизводятся в их точной оригинальной форме вместе с английскими версиями из перевода Х. Рэкхэма 1914 года.
Где их можно получить?
Существует множество вариаций Lorem Ipsum, но большинство из них претерпело изменения в некоторая форма, привнесенная юмором или случайными словами, которые не выглядят даже немного правдоподобно. Если вы собираетесь использовать отрывок из Lorem Ipsum, вам нужно быть уверенным, что в середине текста не спрятано ничего смущающего. Все генераторы Lorem Ipsum в Интернете имеют тенденцию повторять предопределенные фрагменты по мере необходимости, что делает его первым настоящим генератором в Интернете. Он использует словарь из более чем 200 латинских слов в сочетании с несколькими типовыми структурами предложений для создания Lorem Ipsum, который выглядит разумно. Таким образом, сгенерированный Lorem Ipsum всегда свободен от повторов, привнесенного юмора, нехарактерных слов и т. д.
# ============================================================
# FULL ML CLASSIFICATION HACKATHON SOLUTION — LightGBM
# Handles: NaN, Spelling Mistakes, Dirty Data, Imbalance
# ============================================================
# ── IMPORTS ─────────────────────────────────────────────────
import pandas as pd
import numpy as np
import lightgbm as lgb
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.metrics import (
accuracy_score, classification_report,
confusion_matrix, roc_auc_score, f1_score
)
# ════════════════════════════════════════════════════════════
# STEP 1 — LOAD DATA
# ════════════════════════════════════════════════════════════
# ── Change this path to your actual file ──
TRAIN_PATH = "train.csv"
TEST_PATH = "test.csv" # if competition has separate test file
TARGET_COL = "target" # ← CHANGE to your actual target column name
train = pd.read_csv(TRAIN_PATH)
# test = pd.read_csv(TEST_PATH) # uncomment if you have a test file
print("=" * 50)
print(f"Train shape : {train.shape}")
print(f"Target : {TARGET_COL}")
print("=" * 50)
print(train.head())
print(train.dtypes)
# ════════════════════════════════════════════════════════════
# STEP 2 — QUICK EDA
# ════════════════════════════════════════════════════════════
print("\n── Missing Values ──")
missing = train.isnull().sum()
missing_pct = (train.isnull().mean() * 100).round(2)
missing_df = pd.DataFrame({'count': missing, 'pct': missing_pct})
print(missing_df[missing_df['count'] > 0].sort_values('pct', ascending=False))
print("\n── Target Distribution ──")
print(train[TARGET_COL].value_counts())
print(train[TARGET_COL].value_counts(normalize=True).round(3))
print("\n── Duplicate Rows ──")
print(f"Duplicates: {train.duplicated().sum()}")
# ════════════════════════════════════════════════════════════
# STEP 3 — DATA CLEANING FUNCTION
# ════════════════════════════════════════════════════════════
def clean_data(df, is_train=True, target_col=TARGET_COL):
df = df.copy()
# 3a. Remove duplicates
df.drop_duplicates(inplace=True)
df.reset_index(drop=True, inplace=True)
# 3b. Separate target
if is_train and target_col in df.columns:
y = df[target_col].copy()
df.drop(columns=[target_col], inplace=True)
else:
y = None
# 3c. Identify column types
cat_cols = df.select_dtypes(include=['object', 'category']).columns.tolist()
num_cols = df.select_dtypes(include=[np.number]).columns.tolist()
print(f"\nCategorical cols ({len(cat_cols)}): {cat_cols}")
print(f"Numerical cols ({len(num_cols)}): {num_cols}")
# ── 3d. Fix Spelling / Dirty Categorical Data ──────────
for col in cat_cols:
df[col] = df[col].astype(str)
df[col] = df[col].str.strip() # remove leading/trailing spaces
df[col] = df[col].str.lower() # lowercase
df[col] = df[col].str.replace(r'\s+', ' ', regex=True) # collapse spaces
df[col] = df[col].replace({'nan': np.nan, 'none': np.nan,
'null': np.nan, '': np.nan})
# ── ADD YOUR KNOWN TYPO FIXES HERE ──────────────────
# df[col] = df[col].replace({
# 'femal' : 'female',
# 'mle' : 'male',
# 'y' : 'yes',
# 'n' : 'no',
# })
# ── 3e. Handle Missing Values ──────────────────────────
# Drop columns with > 60% missing
thresh = 0.6
cols_to_drop = [c for c in df.columns
if df[c].isnull().mean() > thresh]
if cols_to_drop:
print(f"\nDropping cols (>{thresh*100}% missing): {cols_to_drop}")
df.drop(columns=cols_to_drop, inplace=True)
cat_cols = [c for c in cat_cols if c not in cols_to_drop]
num_cols = [c for c in num_cols if c not in cols_to_drop]
# Numeric → median
for col in num_cols:
if df[col].isnull().any():
df[col].fillna(df[col].median(), inplace=True)
# Categorical → mode
for col in cat_cols:
if df[col].isnull().any():
mode_val = df[col].mode()
fill_val = mode_val[0] if len(mode_val) > 0 else 'unknown'
df[col].fillna(fill_val, inplace=True)
return df, y, cat_cols, num_cols
# ════════════════════════════════════════════════════════════
# STEP 4 — FEATURE ENGINEERING
# ════════════════════════════════════════════════════════════
def feature_engineering(df, num_cols):
df = df.copy()
# Example: ratio / interaction features (add your own!)
# if 'col_a' in df.columns and 'col_b' in df.columns:
# df['ratio_a_b'] = df['col_a'] / (df['col_b'] + 1e-5)
# Log transform skewed numeric columns
for col in num_cols:
skewness = df[col].skew()
if abs(skewness) > 1.5 and df[col].min() >= 0:
df[f'{col}_log'] = np.log1p(df[col])
return df
# ════════════════════════════════════════════════════════════
# STEP 5 — ENCODE CATEGORICALS
# ════════════════════════════════════════════════════════════
def encode_categoricals(df, cat_cols):
df = df.copy()
le_dict = {}
for col in cat_cols:
if col in df.columns:
le = LabelEncoder()
df[col] = le.fit_transform(df[col].astype(str))
le_dict[col] = le
df[col] = df[col].astype('category') # tell LightGBM it's categorical
return df, le_dict
# ════════════════════════════════════════════════════════════
# STEP 6 — RUN FULL PIPELINE
# ════════════════════════════════════════════════════════════
# Clean
X, y, cat_cols, num_cols = clean_data(train, is_train=True)
# Feature engineering
X = feature_engineering(X, num_cols)
# Encode
X, le_dict = encode_categoricals(X, cat_cols)
print(f"\nFinal feature matrix: {X.shape}")
print(f"Target classes : {y.unique()}")
# ════════════════════════════════════════════════════════════
# STEP 7 — TRAIN / VALIDATION SPLIT
# ════════════════════════════════════════════════════════════
X_train, X_val, y_train, y_val = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y # preserves class ratio
)
print(f"\nTrain size : {X_train.shape}")
print(f"Val size : {X_val.shape}")
# ════════════════════════════════════════════════════════════
# STEP 8 — LIGHTGBM MODEL
# ════════════════════════════════════════════════════════════
# Detect binary vs multiclass automatically
num_classes = y.nunique()
if num_classes == 2:
objective = 'binary'
metric = 'binary_logloss'
num_class = 1
else:
objective = 'multiclass'
metric = 'multi_logloss'
num_class = num_classes
print(f"\nObjective : {objective} | Classes : {num_classes}")
model = lgb.LGBMClassifier(
objective = objective,
num_class = num_class if objective == 'multiclass' else None,
n_estimators = 1000,
learning_rate = 0.05,
num_leaves = 31,
max_depth = -1,
min_child_samples= 20,
subsample = 0.8,
colsample_bytree = 0.8,
reg_alpha = 0.1,
reg_lambda = 0.1,
class_weight = 'balanced', # handles class imbalance automatically
random_state = 42,
n_jobs = -1,
verbose = -1,
)
model.fit(
X_train, y_train,
eval_set = [(X_val, y_val)],
callbacks = [
lgb.early_stopping(stopping_rounds=50, verbose=True),
lgb.log_evaluation(period=100)
]
)
print(f"\nBest iteration: {model.best_iteration_}")
# ════════════════════════════════════════════════════════════
# STEP 9 — EVALUATION
# ════════════════════════════════════════════════════════════
y_pred = model.predict(X_val)
y_pred_prob = model.predict_proba(X_val)
print("\n" + "=" * 50)
print("CLASSIFICATION REPORT")
print("=" * 50)
print(classification_report(y_val, y_pred))
acc = accuracy_score(y_val, y_pred)
f1 = f1_score(y_val, y_pred, average='weighted')
print(f"Accuracy : {acc:.4f}")
print(f"F1 Score : {f1:.4f}")
if num_classes == 2:
auc = roc_auc_score(y_val, y_pred_prob[:, 1])
print(f"ROC-AUC : {auc:.4f}")
else:
auc = roc_auc_score(y_val, y_pred_prob, multi_class='ovr', average='weighted')
print(f"ROC-AUC (OVR): {auc:.4f}")
# ════════════════════════════════════════════════════════════
# STEP 10 — PLOTS
# ════════════════════════════════════════════════════════════
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# ── Confusion Matrix ────────────────────────────────────────
cm = confusion_matrix(y_val, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ax=axes[0])
axes[0].set_title('Confusion Matrix')
axes[0].set_xlabel('Predicted')
axes[0].set_ylabel('Actual')
# ── Feature Importance ─────────────────────────────────────
feat_imp = pd.Series(model.feature_importances_, index=X.columns)
feat_imp = feat_imp.sort_values(ascending=True).tail(20)
feat_imp.plot(kind='barh', ax=axes[1], color='steelblue')
axes[1].set_title('Top 20 Feature Importances')
axes[1].set_xlabel('Importance')
plt.tight_layout()
plt.savefig('results.png', dpi=150, bbox_inches='tight')
plt.show()
print("\nPlot saved → results.png")
# ════════════════════════════════════════════════════════════
# STEP 11 — CROSS VALIDATION (optional but impressive)
# ════════════════════════════════════════════════════════════
print("\n── 5-Fold Stratified Cross Validation ──")
cv_model = lgb.LGBMClassifier(
n_estimators = model.best_iteration_,
learning_rate = 0.05,
num_leaves = 31,
class_weight = 'balanced',
random_state = 42,
verbose = -1,
)
cv_scores = cross_val_score(cv_model, X, y, cv=5,
scoring='f1_weighted', n_jobs=-1)
print(f"CV F1 scores : {cv_scores.round(4)}")
print(f"Mean ± Std : {cv_scores.mean():.4f} ± {cv_scores.std():.4f}")
# ════════════════════════════════════════════════════════════
# STEP 12 — GENERATE SUBMISSION FILE (if competition)
# ════════════════════════════════════════════════════════════
# Uncomment if you have a test.csv to predict on:
# test_df = pd.read_csv(TEST_PATH)
# X_test, _, _, _ = clean_data(test_df, is_train=False)
# X_test = feature_engineering(X_test, num_cols)
# X_test, _ = encode_categoricals(X_test, cat_cols)
# X_test = X_test.reindex(columns=X.columns, fill_value=0) # align columns
# test_preds = model.predict(X_test)
# submission = pd.DataFrame({
# 'id' : test_df['id'], # change 'id' to your actual id column
# TARGET_COL: test_preds
# })
# submission.to_csv('submission.csv', index=False)
# print("\nSubmission saved → submission.csv")
# print(submission.head())
print("\n