{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.13.15","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport json\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport scipy.stats as stats\nfrom scipy.fftpack import dct\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# التوافق مع جميع إصدارات مكتبة scikit-image على كاجل\ntry:\n    from skimage.feature import graycomatrix, graycoprops, local_binary_pattern\nexcept ImportError:\n    from skimage.feature import greycomatrix as graycomatrix, greycoprops as graycoprops, local_binary_pattern\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# 1. تحديد مسار البيانات في Kaggle\nDATA_DIR = '/kaggle/input/deepfake-detection-challenge/train_sample_videos'\nMETA_PATH = os.path.join(DATA_DIR, 'metadata.json')\n\n# 2. قراءة ملف metadata.json وتحويله إلى جدول Pandas\nwith open(META_PATH, 'r') as f:\n    meta_dict = json.load(f)\n\nmeta_df = pd.DataFrame.from_dict(meta_dict, orient='index').reset_index()\nmeta_df.rename(columns={'index': 'filename'}, inplace=True)\n\n# 3. طباعة التقرير الإحصائي الأولي للميتاداتا\nprint(\"=== التقرير الأولي لبيانات DFDC (Metadata Summary) ===\")\nprint(f\"إجمالي عدد الفيديوهات في العينة: {len(meta_df)}\")\nprint(\"\\nتوزيع الفئات (Class Distribution):\")\nclass_counts = meta_df['label'].value_counts()\nclass_percents = meta_df['label'].value_counts(normalize=True) * 100\nsummary_table = pd.DataFrame({'Count': class_counts, 'Percentage (%)': class_percents.round(2)})\nprint(summary_table)\n\n# التحقق من القيم المفقودة في حقل original\nprint(\"\\nالقيم المفقودة (Missing Values):\")\nprint(meta_df.isnull().sum())\n\n# 4. رسم بياني لتوزيع الفئات (جاهز للورقة البحثية)\nplt.figure(figsize=(7, 4), dpi=120)\nax = sns.countplot(data=meta_df, x='label', palette=['#e74c3c', '#2ecc71'])\nplt.title('Class Imbalance Distribution in DFDC Sample Dataset', fontsize=12, fontweight='bold')\nplt.xlabel('Video Class Label', fontsize=11)\nplt.ylabel('Number of Videos', fontsize=11)\nfor p in ax.patches:\n    ax.annotate(f'{int(p.get_height())} ({p.get_height()/len(meta_df)*100:.1f}%)', \n                (p.get_x() + p.get_width() / 2., p.get_height()), \n                ha='center', va='bottom', fontsize=10, fontweight='bold')\nplt.tight_layout()\nplt.show()\n\nmeta_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T17:34:05.209486Z","iopub.execute_input":"2026-10-14T17:34:05.209866Z","iopub.status.idle":"2026-10-14T17:34:05.223427Z","shell.execute_reply.started":"2026-10-14T17:34:05.209838Z","shell.execute_reply":"2026-10-14T17:34:05.222288Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport json\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport scipy.stats as stats\nfrom scipy.fftpack import dct\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# التوافق مع جميع إصدارات مكتبة scikit-image على كاجل\ntry:\n    from skimage.feature import graycomatrix, graycoprops, local_binary_pattern\nexcept ImportError:\n    from skimage.feature import greycomatrix as graycomatrix, greycoprops as graycoprops, local_binary_pattern\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# 1. تحديد مسار البيانات في Kaggle\nDATA_DIR = '/kaggle/input/deepfake-detection-challenge/train_sample_videos'\nMETA_PATH = os.path.join(DATA_DIR, 'metadata.json')\n\n# 2. قراءة ملف metadata.json وتحويله إلى جدول Pandas\nwith open(META_PATH, 'r') as f:\n    meta_dict = json.load(f)\n\nmeta_df = pd.DataFrame.from_dict(meta_dict, orient='index').reset_index()\nmeta_df.rename(columns={'index': 'filename'}, inplace=True)\n\n# 3. طباعة التقرير الإحصائي الأولي للميتاداتا\nprint(\"=== التقرير الأولي لبيانات DFDC (Metadata Summary) ===\")\nprint(f\"إجمالي عدد الفيديوهات في العينة: {len(meta_df)}\")\nprint(\"\\nتوزيع الفئات (Class Distribution):\")\nclass_counts = meta_df['label'].value_counts()\nclass_percents = meta_df['label'].value_counts(normalize=True) * 100\nsummary_table = pd.DataFrame({'Count': class_counts, 'Percentage (%)': class_percents.round(2)})\nprint(summary_table)\n\n# التحقق من القيم المفقودة في حقل original\nprint(\"\\nالقيم المفقودة (Missing Values):\")\nprint(meta_df.isnull().sum())\n\n# 4. رسم بياني لتوزيع الفئات (جاهز للورقة البحثية)\nplt.figure(figsize=(7, 4), dpi=120)\nax = sns.countplot(data=meta_df, x='label', palette=['#e74c3c', '#2ecc71'])\nplt.title('Class Imbalance Distribution in DFDC Sample Dataset', fontsize=12, fontweight='bold')\nplt.xlabel('Video Class Label', fontsize=11)\nplt.ylabel('Number of Videos', fontsize=11)\nfor p in ax.patches:\n    ax.annotate(f'{int(p.get_height())} ({p.get_height()/len(meta_df)*100:.1f}%)', \n                (p.get_x() + p.get_width() / 2., p.get_height()), \n                ha='center', va='bottom', fontsize=10, fontweight='bold')\nplt.tight_layout()\nplt.show()\n\nmeta_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T17:34:41.203309Z","iopub.execute_input":"2026-10-14T17:34:41.204264Z","iopub.status.idle":"2026-10-14T17:34:41.216801Z","shell.execute_reply.started":"2026-10-14T17:34:41.204232Z","shell.execute_reply":"2026-10-14T17:34:41.215635Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nprint(\"المجلدات الموجودة في كاجل حالياً:\", os.listdir('/kaggle/input'))\nfor root, dirs, files in os.walk('/kaggle/input'):\n    print(root, \"--> عدد الملفات:\", len(files))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T17:36:38.496723Z","iopub.execute_input":"2026-10-14T17:36:38.497452Z","iopub.status.idle":"2026-10-14T17:36:39.761349Z","shell.execute_reply.started":"2026-10-14T17:36:38.497412Z","shell.execute_reply":"2026-10-14T17:36:39.76063Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport json\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport scipy.stats as stats\nfrom scipy.fftpack import dct\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# التوافق مع جميع إصدارات مكتبة scikit-image على كاجل\ntry:\n    from skimage.feature import graycomatrix, graycoprops, local_binary_pattern\nexcept ImportError:\n    from skimage.feature import greycomatrix as graycomatrix, greycoprops as graycoprops, local_binary_pattern\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# 1. المسار الفعلي المؤكد في حسابك على Kaggle\nDATA_DIR = '/kaggle/input/competitions/deepfake-detection-challenge/train_sample_videos'\nMETA_PATH = os.path.join(DATA_DIR, 'metadata.json')\n\n# 2. قراءة ملف metadata.json وتحويله إلى جدول Pandas\nwith open(META_PATH, 'r') as f:\n    meta_dict = json.load(f)\n\nmeta_df = pd.DataFrame.from_dict(meta_dict, orient='index').reset_index()\nmeta_df.rename(columns={'index': 'filename'}, inplace=True)\n\n# 3. طباعة التقرير الإحصائي الأولي للميتاداتا\nprint(\"=== التقرير الأولي لبيانات DFDC (Metadata Summary) ===\")\nprint(f\"إجمالي عدد الفيديوهات في العينة: {len(meta_df)}\")\nprint(\"\\nتوزيع الفئات (Class Distribution):\")\nclass_counts = meta_df['label'].value_counts()\nclass_percents = meta_df['label'].value_counts(normalize=True) * 100\nsummary_table = pd.DataFrame({'Count': class_counts, 'Percentage (%)': class_percents.round(2)})\nprint(summary_table)\n\n# التحقق من القيم المفقودة وتكرار الفيديوهات الأصلية\nprint(\"\\nالقيم المفقودة (Missing Values):\")\nprint(meta_df.isnull().sum())\nprint(f\"\\nعدد الفيديوهات الأصلية الفريدة التي اشتُقت منها الفيديوهات المزيفة: {meta_df['original'].nunique()}\")\n\n# 4. رسم بياني لتوزيع الفئات (جاهز للورقة البحثية)\nplt.figure(figsize=(7, 4.5), dpi=120)\nax = sns.countplot(data=meta_df, x='label', palette=['#e74c3c', '#2ecc71'])\nplt.title('Class Imbalance Distribution in DFDC Sample Dataset (n=400)', fontsize=12, fontweight='bold')\nplt.xlabel('Video Class Label', fontsize=11)\nplt.ylabel('Number of Videos', fontsize=11)\nfor p in ax.patches:\n    ax.annotate(f'{int(p.get_height())} ({p.get_height()/len(meta_df)*100:.1f}%)', \n                (p.get_x() + p.get_width() / 2., p.get_height()), \n                ha='center', va='bottom', fontsize=10, fontweight='bold')\nplt.ylim(0, 370)\nplt.tight_layout()\nplt.show()\n\ndisplay(meta_df.head(10))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T17:38:58.501811Z","iopub.execute_input":"2026-10-14T17:38:58.502534Z","iopub.status.idle":"2026-10-14T17:38:58.855446Z","shell.execute_reply.started":"2026-10-14T17:38:58.502505Z","shell.execute_reply":"2026-10-14T17:38:58.854826Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# تحميل مصنف الوجه المدمج في OpenCV\nface_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')\n\ndef crop_face_or_center(frame, target_size=(224, 224)):\n    \"\"\"اكتشاف الوجه واقتصاصه، وفي حال عدم ظهوره يأخذ مركز الإطار\"\"\"\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=4, minSize=(60, 60))\n    if len(faces) > 0:\n        x, y, w, h = sorted(faces, key=lambda b: b[2]*b[3], reverse=True)[0]\n        face_img = frame[y:y+h, x:x+w]\n    else:\n        h, w, _ = frame.shape\n        ch, cw = h // 2, w // 2\n        face_img = frame[max(0, ch-112):min(h, ch+112), max(0, cw-112):min(w, cw+112)]\n    return cv2.resize(face_img, target_size)\n\ndef extract_frame_features(bgr_img):\n    \"\"\"استخراج خصائص معالجة الصور من إطار الوجه المقصوص\"\"\"\n    feats = {}\n    gray = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2GRAY)\n    hsv = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HSV)\n    ycbcr = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2YCrCb)\n    \n    # 1. خصائص الفضاء اللوني والإضاءة (Color Space Statistics)\n    feats['Y_mean'] = np.mean(ycbcr[:, :, 0])\n    feats['Cr_mean'] = np.mean(ycbcr[:, :, 1])\n    feats['Cr_std'] = np.std(ycbcr[:, :, 1])\n    feats['Cr_skew'] = float(stats.skew(ycbcr[:, :, 1].flatten()))\n    feats['Cb_mean'] = np.mean(ycbcr[:, :, 2])\n    feats['Cb_std'] = np.std(ycbcr[:, :, 2])\n    feats['Sat_mean'] = np.mean(hsv[:, :, 1])\n    feats['Sat_std'] = np.std(hsv[:, :, 1])\n    feats['Sat_kurtosis'] = float(stats.kurtosis(hsv[:, :, 1].flatten()))\n    \n    # 2. خصائص الحواف والحدة (Edges & Sharpness)\n    laplacian = cv2.Laplacian(gray, cv2.CV_64F)\n    feats['Laplacian_var'] = laplacian.var()\n    sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)\n    sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)\n    sobel_mag = np.sqrt(sobelx**2 + sobely**2)\n    feats['Sobel_mean'] = np.mean(sobel_mag)\n    feats['Sobel_std'] = np.std(sobel_mag)\n    edges = cv2.Canny(gray, 100, 200)\n    feats['Canny_density'] = np.sum(edges > 0) / float(gray.size)\n    \n    # 3. خصائص مجال التردد (Frequency Domain: FFT & DCT)\n    f_transform = np.fft.fft2(gray)\n    f_shift = np.fft.fftshift(f_transform)\n    magnitude_spectrum = 20 * np.log(np.abs(f_shift) + 1e-8)\n    feats['FFT_energy_mean'] = np.mean(magnitude_spectrum)\n    feats['FFT_energy_std'] = np.std(magnitude_spectrum)\n    \n    dct_matrix = dct(dct(gray.astype(float).T, norm='ortho').T, norm='ortho')\n    total_dct_energy = np.sum(np.abs(dct_matrix)) + 1e-8\n    high_freq_dct = np.sum(np.abs(dct_matrix[112:, 112:]))\n    feats['DCT_high_freq_ratio'] = high_freq_dct / total_dct_energy\n    \n    # 4. خصائص النسيج الجنائية (Texture: GLCM & LBP)\n    glcm = graycomatrix(gray, distances=[1], angles=[0, np.pi/4, np.pi/2, 3*np.pi/4], \n                        levels=256, symmetric=True, normed=True)\n    feats['GLCM_contrast'] = np.mean(graycoprops(glcm, 'contrast'))\n    feats['GLCM_homogeneity'] = np.mean(graycoprops(glcm, 'homogeneity'))\n    feats['GLCM_energy'] = np.mean(graycoprops(glcm, 'energy'))\n    feats['GLCM_correlation'] = np.mean(graycoprops(glcm, 'correlation'))\n    \n    lbp = local_binary_pattern(gray, P=8, R=1, method='uniform')\n    lbp_hist, _ = np.histogram(lbp.ravel(), bins=np.arange(0, 11), density=True)\n    feats['LBP_entropy'] = float(stats.entropy(lbp_hist + 1e-8))\n    \n    # 5. ضوضاء المستشعر الدقيقة (Noise Residual Variance)\n    denoised = cv2.GaussianBlur(gray, (5, 5), 0)\n    noise_residual = gray.astype(float) - denoised.astype(float)\n    feats['Noise_var'] = np.var(noise_residual)\n    feats['Noise_kurtosis'] = float(stats.kurtosis(noise_residual.flatten()))\n    \n    return feats, gray\n\ndef process_video_to_row(video_path, num_frames=6):\n    \"\"\"استخراج متوسط الخصائص المكانية + التباين الزمني عبر إطارات الفيديو\"\"\"\n    cap = cv2.VideoCapture(video_path)\n    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n    if total_frames <= 0:\n        cap.release()\n        return None\n    \n    frame_indices = np.linspace(0, total_frames - 1, num_frames, dtype=int)\n    frame_feats_list = []\n    prev_gray = None\n    frame_diffs = []\n    \n    for idx in frame_indices:\n        cap.set(cv2.CAP_PROP_POS_FRAMES, idx)\n        ret, frame = cap.read()\n        if not ret:\n            continue\n        face_crop = crop_face_or_center(frame)\n        f_dict, curr_gray = extract_frame_features(face_crop)\n        frame_feats_list.append(f_dict)\n        \n        if prev_gray is not None:\n            diff = np.mean(np.abs(curr_gray.astype(float) - prev_gray.astype(float)))\n            frame_diffs.append(diff)\n        prev_gray = curr_gray\n        \n    cap.release()\n    if len(frame_feats_list) == 0:\n        return None\n        \n    df_temp = pd.DataFrame(frame_feats_list)\n    row_dict = {}\n    \n    for col in df_temp.columns:\n        row_dict[f'{col}_mean'] = df_temp[col].mean()\n        \n    temporal_cols = ['Laplacian_var', 'Cr_mean', 'GLCM_contrast', 'DCT_high_freq_ratio', 'Noise_var']\n    for col in temporal_cols:\n        row_dict[f'{col}_temporal_std'] = df_temp[col].std()\n        \n    row_dict['InterFrame_Diff_mean'] = np.mean(frame_diffs) if frame_diffs else 0.0\n    row_dict['InterFrame_Diff_std'] = np.std(frame_diffs) if frame_diffs else 0.0\n    \n    return row_dict\n\n# معالجة جميع الفيديوهات الـ 400 في العينة لبناء قاعدة البيانات الجدولية الكاملة\nextracted_rows = []\ntotal_vids = len(meta_df)\nprint(f\"🚀 جاري استخراج الخصائص الرياضية من {total_vids} فيديو لبناء جدول البيانات...\")\n\nfor i, row in meta_df.iterrows():\n    v_path = os.path.join(DATA_DIR, row['filename'])\n    feats = process_video_to_row(v_path, num_frames=6)\n    if feats is not None:\n        feats['filename'] = row['filename']\n        feats['label'] = row['label']\n        feats['target'] = 1 if row['label'] == 'FAKE' else 0\n        extracted_rows.append(feats)\n    if (i + 1) % 40 == 0 or (i + 1) == total_vids:\n        print(f\"✅ تمت معالجة {i + 1} / {total_vids} فيديو...\")\n\nfeatures_df = pd.DataFrame(extracted_rows)\nfeatures_df.to_csv('/kaggle/working/dfdc_tabular_features.csv', index=False)\nprint(f\"\\n🎉 اكتمل بناء قاعدة البيانات الجدولية! الأبعاد: {features_df.shape[0]} فيديو (صف) × {features_df.shape[1]} خاصية (عمود).\")\ndisplay(features_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T17:41:34.105292Z","iopub.execute_input":"2026-10-14T17:41:34.105858Z","iopub.status.idle":"2026-10-14T18:07:57.147344Z","shell.execute_reply.started":"2026-10-14T17:41:34.105832Z","shell.execute_reply":"2026-10-14T18:07:57.146386Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from statsmodels.stats.outliers_influence import variance_inflation_factor\nfrom sklearn.preprocessing import StandardScaler\n\nfeature_cols = [c for c in features_df.columns if c not in ['filename', 'label', 'target']]\n\n# 1. حساب معامل تضخم التباين (VIF) لفحص التعدد الخطي بين الخصائص\nX_clean = features_df[feature_cols].fillna(0)\nX_std = pd.DataFrame(StandardScaler().fit_transform(X_clean), columns=feature_cols)\nvif_dict = {}\nfor idx, col in enumerate(feature_cols):\n    try:\n        vif_dict[col] = variance_inflation_factor(X_std.values, idx)\n    except Exception:\n        vif_dict[col] = np.nan\n\n# 2. إجراء اختبارات الفرضيات الإحصائية وحساب حجم الأثر لكل خاصية من الـ 30 خاصية\nstat_results = []\nreal_sub = features_df[features_df['label'] == 'REAL']\nfake_sub = features_df[features_df['label'] == 'FAKE']\n\nfor col in feature_cols:\n    r_vals = real_sub[col].dropna()\n    f_vals = fake_sub[col].dropna()\n    \n    # اختبار Mann-Whitney U (اللامعلمي) واختبار ANOVA F-test (المعلمي)\n    u_stat, p_val_mw = stats.mannwhitneyu(r_vals, f_vals, alternative='two-sided')\n    f_stat, p_val_anova = stats.f_oneway(r_vals, f_vals)\n    \n    # حساب حجم الأثر الإحصائي (Cohen's d Effect Size)\n    nx, ny = len(r_vals), len(f_vals)\n    dof = nx + ny - 2\n    pool_sd = np.sqrt(((nx - 1) * np.var(r_vals, ddof=1) + (ny - 1) * np.var(f_vals, ddof=1)) / dof)\n    cohens_d = np.abs(np.mean(r_vals) - np.mean(f_vals)) / (pool_sd + 1e-8)\n    \n    stat_results.append({\n        'Feature': col,\n        'REAL (Mean ± SD)': f\"{np.mean(r_vals):.3f} ± {np.std(r_vals):.3f}\",\n        'FAKE (Mean ± SD)': f\"{np.mean(f_vals):.3f} ± {np.std(f_vals):.3f}\",\n        'ANOVA_F': round(f_stat, 3),\n        'MannWhitney_p': p_val_mw,\n        'Cohens_d': round(cohens_d, 3),\n        'VIF': round(vif_dict.get(col, np.nan), 2),\n        'Significant (p<0.05)': 'Yes' if p_val_mw < 0.05 else 'No'\n    })\n\nstats_table = pd.DataFrame(stat_results).sort_values(by='Cohens_d', ascending=False).reset_index(drop=True)\nstats_table.to_csv('/kaggle/working/statistical_hypothesis_table.csv', index=False)\n\nprint(f\"عدد الخصائص ذات الدلالة الإحصائية المعنوية (p < 0.05): {(stats_table['Significant (p<0.05)'] == 'Yes').sum()} من أصل {len(stats_table)} خاصية\\n\")\nprint(\"=== الجدول الإحصائي الرئيسي (Table 1) مرتباً حسب قوة الأثر الإحصائي ===\")\ndisplay(stats_table.head(15))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T18:12:56.211333Z","iopub.execute_input":"2026-10-14T18:12:56.211775Z","iopub.status.idle":"2026-10-14T18:12:58.221827Z","shell.execute_reply.started":"2026-10-14T18:12:56.211743Z","shell.execute_reply":"2026-10-14T18:12:58.221136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"top_4_features = stats_table['Feature'].head(4).tolist()\n\n# 1. رسم التوزيعات الإحصائية (Violin + Boxplot) لأقوى 4 خصائص تم اكتشافها\nfig, axes = plt.subplots(2, 2, figsize=(13, 9), dpi=120)\naxes = axes.flatten()\n\nfor idx, col in enumerate(top_4_features):\n    sns.violinplot(data=features_df, x='label', y=col, ax=axes[idx], \n                   palette=['#e74c3c', '#2ecc71'], inner='box', cut=0)\n    d_val = stats_table.loc[stats_table['Feature'] == col, 'Cohens_d'].values[0]\n    p_val = stats_table.loc[stats_table['Feature'] == col, 'MannWhitney_p'].values[0]\n    axes[idx].set_title(f\"{col}\\n(Cohen's d = {d_val:.3f}, p = {p_val:.2e})\", fontsize=11, fontweight='bold')\n    axes[idx].set_xlabel('')\n\nplt.suptitle('Statistical Distribution Comparison of Top Image Forensic Features (REAL vs FAKE)', fontsize=13, fontweight='bold')\nplt.tight_layout()\nplt.show()\n\n# 2. مصفوفة ارتباط سبيرمان (Spearman Rank Correlation Heatmap) لأهم 12 خاصية\ntop_12_features = stats_table['Feature'].head(12).tolist()\nplt.figure(figsize=(11, 8), dpi=120)\ncorr_matrix = features_df[top_12_features].corr(method='spearman')\nmask = np.triu(np.ones_like(corr_matrix, dtype=bool))\nsns.heatmap(corr_matrix, mask=mask, annot=True, fmt='.2f', cmap='coolwarm', vmin=-1, vmax=1, linewidths=0.5)\nplt.title('Spearman Rank Correlation Matrix of Top 12 Extracted Image Features', fontsize=13, fontweight='bold')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T18:15:00.658498Z","iopub.execute_input":"2026-10-14T18:15:00.658833Z","iopub.status.idle":"2026-10-14T18:15:01.988939Z","shell.execute_reply.started":"2026-10-14T18:15:00.65881Z","shell.execute_reply":"2026-10-14T18:15:01.988204Z"}},"outputs":[],"execution_count":null}]}