{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","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":"# =============================================================================\n# RSNA-ICA — analiza metatagów DICOM dla kohorty MRA\n# -----------------------------------------------------------------------------\n# Jedna komórka do Jupytera / Kaggle Notebooka. Wymaga podpiętych danych konkursu\n# \"RSNA Intracranial Aneurysm Detection\" (Add Input). Bez GPU i bez internetu.\n#\n# Co robi:\n#   - czyta NAGŁÓWEK (bez pikseli) 1 pliku DICOM z każdej serii o etykiecie MRA,\n#   - klasyfikuje serie: TOF / TOF po kontraście / CE-MRA / błąd etykiety,\n#   - wypisuje rozkłady: producent, pole, parametry sekwencji, geometria,\n#   - zapisuje tabelę 1 wiersz = 1 seria do mra_tags.csv.\n#\n# Uwagi:\n#   - Philips zapisuje część serii jako Enhanced MR (1 plik = cały wolumen,\n#     parametry w zagnieżdżonych sekwencjach). get_tag() przeszukuje cały\n#     nagłówek, a TOF rozpoznajemy także po ImageType (...\\TOF\\...).\n#   - Pusty ContrastBolusAgent (\"\") NIE oznacza kontrastu. Brak tagu też nie\n#     dowodzi braku kontrastu (anonimizacja) -> \"bez udokumentowanego kontrastu\".\n#   - Parametry sekwencji są zakładane jako stałe w obrębie serii (1 plik wystarcza).\n# =============================================================================\nimport os\nimport numpy as np\nimport pandas as pd\nimport pydicom\nfrom tqdm.auto import tqdm\n\nBASE = \"/kaggle/input/competitions/rsna-intracranial-aneurysm-detection\"   # <- zmień, jeśli dane są gdzie indziej\nOUT_CSV = \"mra_tags.csv\"\n\npd.set_option(\"display.width\", 200, \"display.max_columns\", 30, \"display.max_rows\", 100)\n\n\n# ---------------------------------------------------------------- pomocnicze\ndef get_tag(ds, name):\n    \"\"\"Wartość tagu z najwyższego poziomu albo z zagnieżdżonych sekwencji (Enhanced MR).\"\"\"\n    if name in ds:\n        return ds.get(name)\n    for elem in ds.iterall():\n        if elem.keyword == name:\n            return elem.value\n    return None\n\n\ndef as_str(v):\n    if v is None:\n        return None\n    if isinstance(v, (list, tuple, pydicom.multival.MultiValue)):\n        return \"\\\\\".join(str(x) for x in v)\n    return str(v).strip()\n\n\ndef as_float(v):\n    if isinstance(v, (list, tuple, pydicom.multival.MultiValue)):\n        v = v[0] if len(v) else None\n    try:\n        return float(v)\n    except (TypeError, ValueError):\n        return np.nan\n\n\ndef vendor(m):\n    m = str(m).upper()\n    for key, name in [(\"SIEMENS\", \"Siemens\"), (\"GE\", \"GE\"), (\"PHILIPS\", \"Philips\"),\n                      (\"TOSHIBA\", \"Canon/Toshiba\"), (\"CANON\", \"Canon/Toshiba\"), (\"HITACHI\", \"Hitachi\")]:\n        if key in m:\n            return name\n    return \"Inny/brak\"\n\n\ndef plane(iop):\n    try:\n        v = np.array([float(x) for x in iop])\n        n = np.abs(np.cross(v[:3], v[3:]))\n        return [\"strzałkowa\", \"czołowa\", \"osiowa\"][int(np.argmax(n))]\n    except Exception:\n        return None\n\n\n# ---------------------------------------------------------------- odczyt nagłówków\ntrain = pd.read_csv(f\"{BASE}/train.csv\")\nmra_uids = train.loc[train[\"Modality\"] == \"MRA\", \"SeriesInstanceUID\"].tolist()\n\nSTR_TAGS = [\"SeriesDescription\", \"ContrastBolusAgent\", \"Manufacturer\", \"ManufacturerModelName\",\n            \"MRAcquisitionType\", \"ImageType\", \"ScanningSequence\", \"SequenceVariant\", \"AngioFlag\"]\nNUM_TAGS = [\"RepetitionTime\", \"EchoTime\", \"FlipAngle\", \"MagneticFieldStrength\",\n            \"SliceThickness\", \"SpacingBetweenSlices\", \"PixelSpacing\", \"Rows\", \"Columns\", \"NumberOfFrames\"]\n\nrows = []\nfor uid in tqdm(mra_uids, desc=\"MRA\"):\n    folder = f\"{BASE}/series/{uid}\"\n    try:\n        files = sorted(os.listdir(folder))\n        ds = pydicom.dcmread(f\"{folder}/{files[0]}\", stop_before_pixels=True)\n        r = {\"uid\": uid, \"n_files\": len(files), \"SOPClassUID\": str(ds.get(\"SOPClassUID\", \"\"))}\n        for t in STR_TAGS:\n            r[t] = as_str(get_tag(ds, t))\n        for t in NUM_TAGS:\n            r[t] = as_float(get_tag(ds, t))\n        r[\"plaszczyzna\"] = plane(get_tag(ds, \"ImageOrientationPatient\"))\n        rows.append(r)\n    except Exception as e:\n        rows.append({\"uid\": uid, \"error\": repr(e)[:200]})\n\ndf = pd.DataFrame(rows)\nif \"error\" not in df:\n    df[\"error\"] = None\n\n# ---------------------------------------------------------------- pola pochodne\ndf[\"Vendor\"] = df[\"Manufacturer\"].apply(vendor)\ndf[\"Tesla\"] = df[\"MagneticFieldStrength\"].where(df[\"MagneticFieldStrength\"] < 100,\n                                                df[\"MagneticFieldStrength\"] / 10000)  # Gauss -> T\ndf[\"enhanced_mr\"] = df[\"SOPClassUID\"] == \"1.2.840.10008.5.1.4.1.1.4.1\"\ndf[\"n_przekrojow\"] = df[\"NumberOfFrames\"].fillna(df[\"n_files\"])\ndf[\"odstep_mm\"] = df[\"SpacingBetweenSlices\"].fillna(df[\"SliceThickness\"])\ndf[\"pokrycie_z_mm\"] = df[\"n_przekrojow\"] * df[\"odstep_mm\"]\ndf[\"nakladanie\"] = df[\"SliceThickness\"] > df[\"odstep_mm\"] + 0.01\n\nagent = df[\"ContrastBolusAgent\"].fillna(\"\").str.strip()\ndesc = df[\"SeriesDescription\"].fillna(\"\").str.upper()\nitype = df[\"ImageType\"].fillna(\"\").str.upper()\ndf[\"kontrast_tag\"] = agent != \"\"\ndf[\"post_w_opisie\"] = desc.str.contains(\"POST\")\n\n\ndef klasa(r):\n    d = str(r[\"SeriesDescription\"]).upper()\n    it = str(r[\"ImageType\"]).upper().split(\"\\\\\")\n    if (\"T2\" in it) or (\"T2\" in d and \"TSE\" in d):\n        return \"błąd etykiety (T2)\"\n    if r[\"RepetitionTime\"] < 10:\n        return \"CE-MRA\"\n    is_tof = (r[\"RepetitionTime\"] >= 10) or (\"TOF\" in it)\n    if is_tof and (r[\"kontrast_tag\"] or r[\"post_w_opisie\"]):\n        return \"TOF po kontraście\"\n    if is_tof:\n        return \"TOF\"\n    return \"nieustalone\"\n\n\nok = df[\"error\"].isna()\ndf.loc[ok, \"klasa\"] = df[ok].apply(klasa, axis=1)\ndf.loc[~ok, \"klasa\"] = \"błąd odczytu\"\ndf = df.merge(train[[\"SeriesInstanceUID\", \"Aneurysm Present\", \"PatientAge\", \"PatientSex\"]],\n              left_on=\"uid\", right_on=\"SeriesInstanceUID\", how=\"left\").drop(columns=\"SeriesInstanceUID\")\ndf.to_csv(OUT_CSV, index=False)\n\n\n# ---------------------------------------------------------------- podsumowanie\ndef naglowek(t):\n    print(f\"\\n{'=' * 80}\\n{t}\\n{'=' * 80}\")\n\n\ndef z_procentem(s):\n    vc = s.value_counts(dropna=False)\n    return pd.DataFrame({\"n\": vc, \"%\": (100 * vc / vc.sum()).round(1)})\n\n\ntof = df[df[\"klasa\"].isin([\"TOF\", \"TOF po kontraście\"])]\n\nnaglowek(f\"0. KOHORTA: {len(df)} serii MRA | błędy odczytu: {(~ok).sum()} | \"\n         f\"Enhanced MR (1 plik = wolumen): {df['enhanced_mr'].sum()}\")\n\nnaglowek(\"1. KLASYFIKACJA SEKWENCJI\")\nprint(z_procentem(df[\"klasa\"]))\n\nnaglowek(\"2. ODSETEK TĘTNIAKÓW W KATEGORIACH\")\nprint(df.groupby(\"klasa\")[\"Aneurysm Present\"].agg(n=\"count\", tetniak=\"sum\", odsetek=\"mean\").round(3))\n\nnaglowek(\"3. KONTRAST (tag w nagłówku vs 'POST' w opisie) — cała kohorta\")\nprint(pd.crosstab(df[\"kontrast_tag\"], df[\"post_w_opisie\"], margins=True))\n\nnaglowek(\"4. PRODUCENT\")\nprint(pd.crosstab(df[\"Vendor\"], df[\"klasa\"], margins=True))\n\nnaglowek(\"5. NAJCZĘSTSZE MODELE SKANERÓW (top 15)\")\nprint((df[\"Vendor\"] + \" | \" + df[\"ManufacturerModelName\"].fillna(\"brak\")).value_counts().head(15))\n\nnaglowek(\"6. POLE MAGNETYCZNE [T]\")\nprint(pd.crosstab(df[\"Tesla\"].round(1).fillna(-1).rename(\"Tesla (-1 = brak)\"), df[\"klasa\"], margins=True))\nprint()\nprint(pd.crosstab(df[\"Vendor\"], df[\"Tesla\"].round(1).fillna(-1).rename(\"Tesla\"), margins=True))\n\nnaglowek(\"7. PARAMETRY SEKWENCJI wg klasy (mediana [min–max])\")\ndef med_zakres(x):\n    x = x.dropna()\n    return f\"{x.median():.2f} [{x.min():.2f}–{x.max():.2f}] n={len(x)}\" if len(x) else \"brak\"\nprint(df.groupby(\"klasa\")[[\"RepetitionTime\", \"EchoTime\", \"FlipAngle\"]].agg(med_zakres))\n\nnaglowek(f\"8. GEOMETRIA — tylko TOF (n={len(tof)}), percentyle\")\ngeo_cols = [\"SliceThickness\", \"SpacingBetweenSlices\", \"odstep_mm\", \"PixelSpacing\",\n            \"Rows\", \"Columns\", \"n_przekrojow\", \"pokrycie_z_mm\"]\nprint(tof[geo_cols].describe(percentiles=[.05, .25, .5, .75, .95]).T.round(2))\n\nnaglowek(\"9. GEOMETRIA TOF — przedziały\")\nprint(\"Grubość warstwy [mm]:\")\nprint(z_procentem(pd.cut(tof[\"SliceThickness\"], [0, 0.5, 0.7, 1.0, 1.5, 100])).sort_index())\nprint(\"\\nOdstęp między przekrojami [mm]:\")\nprint(z_procentem(pd.cut(tof[\"odstep_mm\"], [0, 0.3, 0.5, 0.7, 1.0, 100])).sort_index())\nprint(\"\\nRozmiar piksela w płaszczyźnie [mm]:\")\nprint(z_procentem(pd.cut(tof[\"PixelSpacing\"], [0, 0.3, 0.4, 0.5, 0.7, 100])).sort_index())\nprint(\"\\nLiczba przekrojów w serii:\")\nprint(z_procentem(pd.cut(tof[\"n_przekrojow\"], [0, 60, 100, 150, 200, 300, 10000])).sort_index())\nprint(\"\\nNakładanie przekrojów (odstęp < grubość):\")\nprint(z_procentem(tof[\"nakladanie\"]))\n\nnaglowek(\"10. GRUBOŚĆ WARSTWY TOF wg producenta i pola (mediana [mm])\")\nprint(tof.pivot_table(index=\"Vendor\", columns=tof[\"Tesla\"].round(1), values=\"SliceThickness\",\n                      aggfunc=\"median\").round(2))\n\nnaglowek(\"11. PŁASZCZYZNA × 2D/3D (TOF)\")\nprint(pd.crosstab(tof[\"plaszczyzna\"].fillna(\"brak\"), tof[\"MRAcquisitionType\"].fillna(\"brak\"), margins=True))\n\nnaglowek(\"12. ImageType (TOF) — czy są MIP-y / rekonstrukcje (DERIVED)?\")\nprint(tof[\"ImageType\"].fillna(\"brak\").value_counts().head(15))\nprint(\"\\nSerie DERIVED lub z 'MIP' w ImageType/opisie:\",\n      (tof[\"ImageType\"].fillna(\"\").str.upper().str.contains(\"DERIVED|MIP\") |\n       tof[\"SeriesDescription\"].fillna(\"\").str.upper().str.contains(\"MIP\")).sum())\n\nnaglowek(\"13. NAJCZĘSTSZE SeriesDescription (top 20)\")\nprint(desc.value_counts().head(20))\n\nnaglowek(\"14. SERIE SPOZA TOF (do ręcznego przejrzenia)\")\nprint(df.loc[~df[\"klasa\"].isin([\"TOF\", \"TOF po kontraście\"]),\n             [\"klasa\", \"SeriesDescription\", \"ContrastBolusAgent\", \"RepetitionTime\",\n              \"EchoTime\", \"FlipAngle\", \"Vendor\", \"Tesla\"]].to_string())\n\nprint(f\"\\nZapisano: {OUT_CSV}  ({len(df)} wierszy, 1 wiersz = 1 seria)\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T13:17:48.83238Z","iopub.execute_input":"2026-09-24T13:17:48.832965Z","iopub.status.idle":"2026-09-24T13:17:59.57517Z","shell.execute_reply.started":"2026-09-24T13:17:48.832933Z","shell.execute_reply":"2026-09-24T13:17:59.574327Z"}},"outputs":[{"output_type":"display_data","data":{"text/plain":"MRA:   0%|          | 0/1252 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"92918729475b4bf29c14e986a81d623b"}},"metadata":{}},{"name":"stderr","text":"/usr/local/lib/python3.12/dist-packages/pydicom/valuerep.py:440: UserWarning: The value length (20) exceeds the maximum length of 16 allowed for VR SH.\n  warn_and_log(msg)\n/usr/local/lib/python3.12/dist-packages/pydicom/valuerep.py:440: UserWarning: The value length (64) exceeds the maximum length of 16 allowed for VR SH.\n  warn_and_log(msg)\n","output_type":"stream"},{"name":"stdout","text":"\n================================================================================\n0. KOHORTA: 1252 serii MRA | błędy odczytu: 0 | Enhanced MR (1 plik = wolumen): 157\n================================================================================\n\n================================================================================\n1. KLASYFIKACJA SEKWENCJI\n================================================================================\n                       n     %\nklasa                         \nTOF                 1197  95.6\nTOF po kontraście     41   3.3\nCE-MRA                13   1.0\nbłąd etykiety (T2)     1   0.1\n\n================================================================================\n2. ODSETEK TĘTNIAKÓW W KATEGORIACH\n================================================================================\n                       n  tetniak  odsetek\nklasa                                     \nCE-MRA                13        4    0.308\nTOF                 1197      530    0.443\nTOF po kontraście     41       21    0.512\nbłąd etykiety (T2)     1        0    0.000\n\n================================================================================\n3. KONTRAST (tag w nagłówku vs 'POST' w opisie) — cała kohorta\n================================================================================\npost_w_opisie  False  True   All\nkontrast_tag                    \nFalse           1203     1  1204\nTrue              13    35    48\nAll             1216    36  1252\n\n================================================================================\n4. PRODUCENT\n================================================================================\nklasa          CE-MRA   TOF  TOF po kontraście  błąd etykiety (T2)   All\nVendor                                                                  \nCanon/Toshiba       0    70                  0                   0    70\nGE                  3   289                  6                   0   298\nHitachi             0     1                  0                   0     1\nPhilips             1   222                  0                   1   224\nSiemens             9   615                 35                   0   659\nAll                13  1197                 41                   1  1252\n\n================================================================================\n5. NAJCZĘSTSZE MODELE SKANERÓW (top 15)\n================================================================================\nPhilips | Achieva              173\nSiemens | MAGNETOM Vida        108\nSiemens | Skyra                107\nSiemens | Aera                  86\nGE | Optima MR450w              80\nSiemens | Avanto                69\nGE | Signa HDxt                 62\nSiemens | MAGNETOM Vida Fit     53\nGE | SIGNA Artist               52\nSiemens | Espree                49\nSiemens | MAGNETOM Skyra        46\nCanon/Toshiba | Titan           35\nGE | SIGNA Pioneer              31\nSiemens | MAGNETOM Aera         31\nSiemens | Avanto_fit            26\nName: count, dtype: int64\n\n================================================================================\n6. POLE MAGNETYCZNE [T]\n================================================================================\nklasa              CE-MRA   TOF  TOF po kontraście  błąd etykiety (T2)   All\nTesla (-1 = brak)                                                           \n-1.0                    0     5                  0                   0     5\n 1.2                    0     1                  0                   0     1\n 1.5                    8   694                  5                   1   708\n 3.0                    5   497                 36                   0   538\n All                   13  1197                 41                   1  1252\n\nTesla          -1.0   1.2   1.5   3.0   All\nVendor                                     \nCanon/Toshiba     5     0    36    29    70\nGE                0     0   176   122   298\nHitachi           0     1     0     0     1\nPhilips           0     0   194    30   224\nSiemens           0     0   302   357   659\nAll               5     1   708   538  1252\n\n================================================================================\n7. PARAMETRY SEKWENCJI wg klasy (mediana [min–max])\n================================================================================\n                                RepetitionTime                  EchoTime                  FlipAngle\nklasa                                                                                              \nCE-MRA                   3.47 [3.20–5.48] n=13     1.33 [1.12–1.87] n=13   23.00 [17.00–35.00] n=13\nTOF                 23.29 [14.00–47.94] n=1041  3.53 [2.10–17.50] n=1041  20.00 [7.00–70.00] n=1041\nTOF po kontraście     22.00 [21.00–44.88] n=41     3.43 [2.50–7.00] n=41   18.00 [15.00–25.00] n=41\nbłąd etykiety (T2)                        brak                      brak                       brak\n\n================================================================================\n8. GEOMETRIA — tylko TOF (n=1238), percentyle\n================================================================================\n                       count    mean      std    min      5%     25%     50%    75%      95%       max\nSliceThickness        1238.0    0.82     0.39    0.3    0.50    0.50    0.60    1.2     1.60      3.00\nSpacingBetweenSlices  1236.0    5.19     8.89    0.3    0.50    0.50    0.60    0.8    24.09     39.00\nodstep_mm             1238.0    5.18     8.88    0.3    0.50    0.50    0.60    0.8    24.07     39.00\nPixelSpacing          1238.0    0.41     0.10    0.2    0.26    0.35    0.41    0.5     0.60      1.02\nRows                  1238.0  546.65   138.56  240.0  384.00  512.00  512.00  528.0   768.00   1024.00\nColumns               1238.0  520.11   134.96  240.0  300.00  512.00  512.00  528.0   696.00   1024.00\nn_przekrojow          1238.0  176.29    52.50    1.0  108.00  148.00  165.00  204.0   266.00    514.00\npokrycie_z_mm         1238.0  855.43  1537.18    0.5   76.80   90.50  105.00  170.4  4184.55  10177.50\n\n================================================================================\n9. GEOMETRIA TOF — przedziały\n================================================================================\nGrubość warstwy [mm]:\n                  n     %\nSliceThickness           \n(0.0, 0.5]      371  30.0\n(0.5, 0.7]      387  31.3\n(0.7, 1.0]      148  12.0\n(1.0, 1.5]      255  20.6\n(1.5, 100.0]     77   6.2\n\nOdstęp między przekrojami [mm]:\n                n     %\nodstep_mm              \n(0.0, 0.3]      9   0.7\n(0.3, 0.5]    306  24.7\n(0.5, 0.7]    506  40.9\n(0.7, 1.0]    141  11.4\n(1.0, 100.0]  276  22.3\n\nRozmiar piksela w płaszczyźnie [mm]:\n                n     %\nPixelSpacing           \n(0.0, 0.3]    217  17.5\n(0.3, 0.4]    387  31.3\n(0.4, 0.5]    472  38.1\n(0.5, 0.7]    149  12.0\n(0.7, 100.0]   13   1.1\n\nLiczba przekrojów w serii:\n                n     %\nn_przekrojow           \n(0, 60]         2   0.2\n(60, 100]      45   3.6\n(100, 150]    439  35.5\n(150, 200]    431  34.8\n(200, 300]    289  23.3\n(300, 10000]   32   2.6\n\nNakładanie przekrojów (odstęp < grubość):\n              n     %\nnakladanie           \nFalse       828  66.9\nTrue        410  33.1\n\n================================================================================\n10. GRUBOŚĆ WARSTWY TOF wg producenta i pola (mediana [mm])\n================================================================================\nTesla          1.2   1.5  3.0\nVendor                       \nCanon/Toshiba  NaN  1.50  0.6\nGE             NaN  1.40  1.2\nHitachi        1.0   NaN  NaN\nPhilips        NaN  0.55  1.2\nSiemens        NaN  0.60  0.5\n\n================================================================================\n11. PŁASZCZYZNA × 2D/3D (TOF)\n================================================================================\nMRAcquisitionType     2D    3D   All\nplaszczyzna                         \nosiowa             1   1  1236  1238\nAll                1   1  1236  1238\n\n================================================================================\n12. ImageType (TOF) — czy są MIP-y / rekonstrukcje (DERIVED)?\n================================================================================\nImageType\nORIGINAL\\PRIMARY\\OTHER                          365\nORIGINAL\\PRIMARY\\M\\NORM\\DIS2D\\MFSPLIT           277\nORIGINAL\\PRIMARY\\TOF\\NONE                       156\nORIGINAL\\PRIMARY\\M\\ND\\NORM                      129\nORIGINAL\\PRIMARY\\M\\NORM\\DIS2D                   119\nORIGINAL\\PRIMARY\\M\\DIS2D                         94\nORIGINAL\\PRIMARY\\M_FFE\\M\\FFE                     66\nORIGINAL\\PRIMARY\\M\\ND                            22\nORIGINAL\\PRIMARY\\M\\ND\\FM\\FIL                      3\nORIGINAL\\PRIMARY\\M\\ND\\NORM\\SH\\FIL                 2\nORIGINAL\\PRIMARY\\M\\DIS2D\\FM\\FIL                   1\nORIGINAL\\PRIMARY\\M\\ND\\NORM\\FM\\FIL                 1\nORIGINAL\\PRIMARY\\M\\NORM\\DIS2D\\SH\\FIL\\MFSPLIT      1\nORIGINAL\\PRIMARY\\GDC                              1\nORIGINAL\\PRIMARY\\M\\NORM\\DIS2D\\SH\\FIL              1\nName: count, dtype: int64\n\nSerie DERIVED lub z 'MIP' w ImageType/opisie: 0\n\n================================================================================\n13. NAJCZĘSTSZE SeriesDescription (top 20)\n================================================================================\nSeriesDescription\nMRA                         156\n                            145\nTOF_3D_MULTI-SLAB           109\nAX COW                       78\nTOF_FL3D_TRA                 59\nAX TOF COW                   42\nAX TOF COW 0.6               41\nAX 3D TOF BRAIN              31\nAX 3DTOF                     26\nH-AX 3D TOF-COW              25\nBRAIN AX 3D MRA_TOF          23\nTOF_3D_MULTI-SLAB_HEAD       22\nTOF_3D_5X_MULTI-SLAB 384     17\nTOF_CS_ACC5.0                17\n3D TOF MRA NEW               17\nCOW                          15\nAX 3DTOF 3SLAB               13\nAX 3D TOF                    13\nAX TOF 3D - MRA COW          13\nAX 3DTOF 2SLAB               12\nName: count, dtype: int64\n\n================================================================================\n14. SERIE SPOZA TOF (do ręcznego przejrzenia)\n================================================================================\n                   klasa          SeriesDescription ContrastBolusAgent  RepetitionTime  EchoTime  FlipAngle   Vendor  Tesla\n42                CE-MRA                  cemra pre               None          3.4200     1.230       23.0  Siemens    1.5\n165               CE-MRA                 cemra post         Multihance          3.4200     1.230       23.0  Siemens    1.5\n245               CE-MRA                       None               None          5.4798     1.872       35.0  Philips    1.5\n257   błąd etykiety (T2)                     T2 TSE               None             NaN       NaN        NaN  Philips    1.5\n473               CE-MRA        cemra post_TTC=1.0s         MULTIHANCE          3.4700     1.330       17.0  Siemens    3.0\n478               CE-MRA         cemra pre_TTC=1.0s               None          3.4700     1.330       17.0  Siemens    3.0\n479               CE-MRA        cemra post_TTC=1.0s         MULTIHANCE          3.4700     1.330       17.0  Siemens    3.0\n647               CE-MRA                       None               None          3.8560     1.396       25.0       GE    3.0\n773               CE-MRA                       None       DeIdentified          3.2000     1.120       25.0  Siemens    1.5\n872               CE-MRA        cemra post_TTC=1.0s         MULTIHANCE          3.4700     1.330       17.0  Siemens    3.0\n897               CE-MRA                 cemra post         multihance          3.4200     1.230       23.0  Siemens    1.5\n1076              CE-MRA                 cemra post         multihance          3.4200     1.230       23.0  Siemens    1.5\n1222              CE-MRA  Ph1/Cor ceMRA FT elliptic               None          5.0360     1.776       25.0       GE    1.5\n1243              CE-MRA                       None       DeIdentified          4.0560     1.468       25.0       GE    1.5\n\nZapisano: mra_tags.csv  (1252 wierszy, 1 wiersz = 1 seria)\n","output_type":"stream"}],"execution_count":13},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\n\nBASE = \"/kaggle/input/competitions/rsna-intracranial-aneurysm-detection\"\nprint(os.listdir(BASE))\n\ntrain = pd.read_csv(f\"{BASE}/train.csv\")\nprint(train[\"Modality\"].value_counts())","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-09-24T12:55:51.471313Z","iopub.execute_input":"2026-09-24T12:55:51.4719Z","iopub.status.idle":"2026-09-24T12:55:51.923816Z","shell.execute_reply.started":"2026-09-24T12:55:51.471859Z","shell.execute_reply":"2026-09-24T12:55:51.922965Z"}},"outputs":[{"name":"stdout","text":"['train_localizers.csv', 'segmentations', 'series', 'train.csv', 'kaggle_evaluation']\nModality\nCTA           1808\nMRA           1252\nMRI T2         983\nMRI T1post     305\nName: count, dtype: int64\n","output_type":"stream"}],"execution_count":2},{"cell_type":"code","source":"import pydicom\n\ndef get_tag(ds, name):\n    \"\"\"Szuka tagu w całym nagłówku, także w zagnieżdżonych sekwencjach (Philips/Enhanced MR).\"\"\"\n    if name in ds:\n        return ds.get(name)\n    for elem in ds.iterall():\n        if elem.keyword == name:\n            return elem.value\n    return None\n\nuid = train.loc[train[\"Modality\"] == \"MRA\", \"SeriesInstanceUID\"].iloc[0]\nfolder = f\"{BASE}/series/{uid}\"\nfiles = sorted(os.listdir(folder))\nprint(\"Liczba plików w serii:\", len(files))\n\nds = pydicom.dcmread(f\"{folder}/{files[0]}\", stop_before_pixels=True)\nfor tag in [\"SeriesDescription\", \"ContrastBolusAgent\", \"RepetitionTime\",\n            \"EchoTime\", \"FlipAngle\", \"Manufacturer\", \"MagneticFieldStrength\"]:\n    print(f\"{tag:25s} = {get_tag(ds, tag)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T12:55:52.067076Z","iopub.execute_input":"2026-09-24T12:55:52.068034Z","iopub.status.idle":"2026-09-24T12:55:52.982657Z","shell.execute_reply.started":"2026-09-24T12:55:52.068Z","shell.execute_reply":"2026-09-24T12:55:52.981724Z"}},"outputs":[{"name":"stdout","text":"Liczba plików w serii: 188\nSeriesDescription         = AX COW\nContrastBolusAgent        = None\nRepetitionTime            = 24\nEchoTime                  = 7\nFlipAngle                 = 25\nManufacturer              = SIEMENS\nMagneticFieldStrength     = 1.5\n","output_type":"stream"}],"execution_count":3},{"cell_type":"code","source":"from tqdm import tqdm\n\nTAGS = [\"SeriesDescription\", \"ContrastBolusAgent\", \"RepetitionTime\", \"EchoTime\",\n        \"FlipAngle\", \"Manufacturer\", \"MagneticFieldStrength\"]\n\nrows = []\nfor uid in tqdm(train.loc[train[\"Modality\"] == \"MRA\", \"SeriesInstanceUID\"]):\n    folder = f\"{BASE}/series/{uid}\"\n    try:\n        f = sorted(os.listdir(folder))[0]\n        ds = pydicom.dcmread(f\"{folder}/{f}\", stop_before_pixels=True)\n        row = {\"uid\": uid, \"n_files\": len(os.listdir(folder))}\n        for t in TAGS:\n            v = get_tag(ds, t)\n            row[t] = str(v) if v is not None else None\n        rows.append(row)\n    except Exception as e:\n        rows.append({\"uid\": uid, \"error\": str(e)})\n\nmra_meta = pd.DataFrame(rows)\nmra_meta.to_csv(\"mra_meta.csv\", index=False)   # zapis, żeby nie liczyć drugi raz\nmra_meta.head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T12:55:52.984366Z","iopub.execute_input":"2026-09-24T12:55:52.984683Z","iopub.status.idle":"2026-09-24T12:57:18.140604Z","shell.execute_reply.started":"2026-09-24T12:55:52.984655Z","shell.execute_reply":"2026-09-24T12:57:18.139756Z"}},"outputs":[{"name":"stderr","text":"  1%|          | 9/1252 [00:00<01:39, 12.44it/s]/usr/local/lib/python3.12/dist-packages/pydicom/valuerep.py:440: UserWarning: The value length (20) exceeds the maximum length of 16 allowed for VR SH.\n  warn_and_log(msg)\n  4%|▎         | 44/1252 [00:03<01:18, 15.34it/s]/usr/local/lib/python3.12/dist-packages/pydicom/valuerep.py:440: UserWarning: The value length (64) exceeds the maximum length of 16 allowed for VR SH.\n  warn_and_log(msg)\n100%|██████████| 1252/1252 [01:25<00:00, 14.71it/s]\n","output_type":"stream"},{"execution_count":4,"output_type":"execute_result","data":{"text/plain":"                                                 uid  n_files  \\\n0  1.2.826.0.1.3680043.8.498.10004044428023505108...      188   \n1  1.2.826.0.1.3680043.8.498.10004684224894397679...      147   \n2  1.2.826.0.1.3680043.8.498.10009383108068795488...      224   \n3  1.2.826.0.1.3680043.8.498.10012790035410518400...        1   \n4  1.2.826.0.1.3680043.8.498.10022688097731894079...      178   \n5  1.2.826.0.1.3680043.8.498.10023411164590664678...      205   \n6  1.2.826.0.1.3680043.8.498.10035782880104673269...        1   \n7  1.2.826.0.1.3680043.8.498.10037266473301611864...      201   \n8  1.2.826.0.1.3680043.8.498.10040419508532196461...      188   \n9  1.2.826.0.1.3680043.8.498.10042423585566957032...      116   \n\n      SeriesDescription ContrastBolusAgent RepetitionTime EchoTime FlipAngle  \\\n0                AX COW               None             24        7        25   \n1         3D TOF Brain*               None             23      2.7        20   \n2  AX 3D TOF WHOLE HEAD               None             21     3.42        20   \n3                   MRA               None           None     None      None   \n4            ax tof cow               None             21     3.43        18   \n5          TOF_FL3D_TRA               None          44.88      3.5        15   \n6                   MRA               None           None     None      None   \n7            AX COW PRE               None             22      3.6        18   \n8              ax 3dtof               None             20        3        20   \n9         TOF_3D MRA .7               None             22     7.15        25   \n\n              Manufacturer MagneticFieldStrength  \n0                  SIEMENS                   1.5  \n1       GE MEDICAL SYSTEMS                   1.5  \n2     Siemens Healthineers                     3  \n3  Philips Medical Systems                   1.5  \n4                  SIEMENS                     3  \n5     Siemens Healthineers                     3  \n6  Philips Medical Systems                   1.5  \n7                  SIEMENS                     3  \n8       GE MEDICAL SYSTEMS                   1.5  \n9                  SIEMENS                   1.5  ","text/html":"<div>\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }\n\n    .dataframe tbody tr th {\n        vertical-align: top;\n    }\n\n    .dataframe thead th {\n        text-align: right;\n    }\n</style>\n<table border=\"1\" class=\"dataframe\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th></th>\n      <th>uid</th>\n      <th>n_files</th>\n      <th>SeriesDescription</th>\n      <th>ContrastBolusAgent</th>\n      <th>RepetitionTime</th>\n      <th>EchoTime</th>\n      <th>FlipAngle</th>\n      <th>Manufacturer</th>\n      <th>MagneticFieldStrength</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>0</th>\n      <td>1.2.826.0.1.3680043.8.498.10004044428023505108...</td>\n      <td>188</td>\n      <td>AX COW</td>\n      <td>None</td>\n      <td>24</td>\n      <td>7</td>\n      <td>25</td>\n      <td>SIEMENS</td>\n      <td>1.5</td>\n    </tr>\n    <tr>\n      <th>1</th>\n      <td>1.2.826.0.1.3680043.8.498.10004684224894397679...</td>\n      <td>147</td>\n      <td>3D TOF Brain*</td>\n      <td>None</td>\n      <td>23</td>\n      <td>2.7</td>\n      <td>20</td>\n      <td>GE MEDICAL SYSTEMS</td>\n      <td>1.5</td>\n    </tr>\n    <tr>\n      <th>2</th>\n      <td>1.2.826.0.1.3680043.8.498.10009383108068795488...</td>\n      <td>224</td>\n      <td>AX 3D TOF WHOLE HEAD</td>\n      <td>None</td>\n      <td>21</td>\n      <td>3.42</td>\n      <td>20</td>\n      <td>Siemens Healthineers</td>\n      <td>3</td>\n    </tr>\n    <tr>\n      <th>3</th>\n      <td>1.2.826.0.1.3680043.8.498.10012790035410518400...</td>\n      <td>1</td>\n      <td>MRA</td>\n      <td>None</td>\n      <td>None</td>\n      <td>None</td>\n      <td>None</td>\n      <td>Philips Medical Systems</td>\n      <td>1.5</td>\n    </tr>\n    <tr>\n      <th>4</th>\n      <td>1.2.826.0.1.3680043.8.498.10022688097731894079...</td>\n      <td>178</td>\n      <td>ax tof cow</td>\n      <td>None</td>\n      <td>21</td>\n      <td>3.43</td>\n      <td>18</td>\n      <td>SIEMENS</td>\n      <td>3</td>\n    </tr>\n    <tr>\n      <th>5</th>\n      <td>1.2.826.0.1.3680043.8.498.10023411164590664678...</td>\n      <td>205</td>\n      <td>TOF_FL3D_TRA</td>\n      <td>None</td>\n      <td>44.88</td>\n      <td>3.5</td>\n      <td>15</td>\n      <td>Siemens Healthineers</td>\n      <td>3</td>\n    </tr>\n    <tr>\n      <th>6</th>\n      <td>1.2.826.0.1.3680043.8.498.10035782880104673269...</td>\n      <td>1</td>\n      <td>MRA</td>\n      <td>None</td>\n      <td>None</td>\n      <td>None</td>\n      <td>None</td>\n      <td>Philips Medical Systems</td>\n      <td>1.5</td>\n    </tr>\n    <tr>\n      <th>7</th>\n      <td>1.2.826.0.1.3680043.8.498.10037266473301611864...</td>\n      <td>201</td>\n      <td>AX COW PRE</td>\n      <td>None</td>\n      <td>22</td>\n      <td>3.6</td>\n      <td>18</td>\n      <td>SIEMENS</td>\n      <td>3</td>\n    </tr>\n    <tr>\n      <th>8</th>\n      <td>1.2.826.0.1.3680043.8.498.10040419508532196461...</td>\n      <td>188</td>\n      <td>ax 3dtof</td>\n      <td>None</td>\n      <td>20</td>\n      <td>3</td>\n      <td>20</td>\n      <td>GE MEDICAL SYSTEMS</td>\n      <td>1.5</td>\n    </tr>\n    <tr>\n      <th>9</th>\n      <td>1.2.826.0.1.3680043.8.498.10042423585566957032...</td>\n      <td>116</td>\n      <td>TOF_3D MRA .7</td>\n      <td>None</td>\n      <td>22</td>\n      <td>7.15</td>\n      <td>25</td>\n      <td>SIEMENS</td>\n      <td>1.5</td>\n    </tr>\n  </tbody>\n</table>\n</div>"},"metadata":{}}],"execution_count":4},{"cell_type":"code","source":"mra_meta.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T12:57:18.14211Z","iopub.execute_input":"2026-09-24T12:57:18.14241Z","iopub.status.idle":"2026-09-24T12:57:18.154778Z","shell.execute_reply.started":"2026-09-24T12:57:18.142386Z","shell.execute_reply":"2026-09-24T12:57:18.153817Z"}},"outputs":[{"execution_count":5,"output_type":"execute_result","data":{"text/plain":"                                                 uid  n_files  \\\n0  1.2.826.0.1.3680043.8.498.10004044428023505108...      188   \n1  1.2.826.0.1.3680043.8.498.10004684224894397679...      147   \n2  1.2.826.0.1.3680043.8.498.10009383108068795488...      224   \n3  1.2.826.0.1.3680043.8.498.10012790035410518400...        1   \n4  1.2.826.0.1.3680043.8.498.10022688097731894079...      178   \n\n      SeriesDescription ContrastBolusAgent RepetitionTime EchoTime FlipAngle  \\\n0                AX COW               None             24        7        25   \n1         3D TOF Brain*               None             23      2.7        20   \n2  AX 3D TOF WHOLE HEAD               None             21     3.42        20   \n3                   MRA               None           None     None      None   \n4            ax tof cow               None             21     3.43        18   \n\n              Manufacturer MagneticFieldStrength  \n0                  SIEMENS                   1.5  \n1       GE MEDICAL SYSTEMS                   1.5  \n2     Siemens Healthineers                     3  \n3  Philips Medical Systems                   1.5  \n4                  SIEMENS                     3  ","text/html":"<div>\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }\n\n    .dataframe tbody tr th {\n        vertical-align: top;\n    }\n\n    .dataframe thead th {\n        text-align: right;\n    }\n</style>\n<table border=\"1\" class=\"dataframe\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th></th>\n      <th>uid</th>\n      <th>n_files</th>\n      <th>SeriesDescription</th>\n      <th>ContrastBolusAgent</th>\n      <th>RepetitionTime</th>\n      <th>EchoTime</th>\n      <th>FlipAngle</th>\n      <th>Manufacturer</th>\n      <th>MagneticFieldStrength</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>0</th>\n      <td>1.2.826.0.1.3680043.8.498.10004044428023505108...</td>\n      <td>188</td>\n      <td>AX COW</td>\n      <td>None</td>\n      <td>24</td>\n      <td>7</td>\n      <td>25</td>\n      <td>SIEMENS</td>\n      <td>1.5</td>\n    </tr>\n    <tr>\n      <th>1</th>\n      <td>1.2.826.0.1.3680043.8.498.10004684224894397679...</td>\n      <td>147</td>\n      <td>3D TOF Brain*</td>\n      <td>None</td>\n      <td>23</td>\n      <td>2.7</td>\n      <td>20</td>\n      <td>GE MEDICAL SYSTEMS</td>\n      <td>1.5</td>\n    </tr>\n    <tr>\n      <th>2</th>\n      <td>1.2.826.0.1.3680043.8.498.10009383108068795488...</td>\n      <td>224</td>\n      <td>AX 3D TOF WHOLE HEAD</td>\n      <td>None</td>\n      <td>21</td>\n      <td>3.42</td>\n      <td>20</td>\n      <td>Siemens Healthineers</td>\n      <td>3</td>\n    </tr>\n    <tr>\n      <th>3</th>\n      <td>1.2.826.0.1.3680043.8.498.10012790035410518400...</td>\n      <td>1</td>\n      <td>MRA</td>\n      <td>None</td>\n      <td>None</td>\n      <td>None</td>\n      <td>None</td>\n      <td>Philips Medical Systems</td>\n      <td>1.5</td>\n    </tr>\n    <tr>\n      <th>4</th>\n      <td>1.2.826.0.1.3680043.8.498.10022688097731894079...</td>\n      <td>178</td>\n      <td>ax tof cow</td>\n      <td>None</td>\n      <td>21</td>\n      <td>3.43</td>\n      <td>18</td>\n      <td>SIEMENS</td>\n      <td>3</td>\n    </tr>\n  </tbody>\n</table>\n</div>"},"metadata":{}}],"execution_count":5},{"cell_type":"code","source":"for c in [\"RepetitionTime\", \"EchoTime\", \"FlipAngle\"]:\n    mra_meta[c] = pd.to_numeric(mra_meta[c], errors=\"coerce\")\n\nprint(\"Kontrast wpisany w nagłówku:\", mra_meta[\"ContrastBolusAgent\"].notna().sum(), \"/\", len(mra_meta))\nprint(\"\\nTR < 10 ms (podejrzenie CE-MRA):\", (mra_meta[\"RepetitionTime\"] < 10).sum())\nprint(\"TR >= 10 ms (podejrzenie TOF):  \", (mra_meta[\"RepetitionTime\"] >= 10).sum())\nprint(\"\\nNajczęstsze opisy serii:\")\nprint(mra_meta[\"SeriesDescription\"].str.upper().value_counts().head(20))\nprint(\"\\nProducenci:\")\nprint(mra_meta[\"Manufacturer\"].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T12:57:18.155931Z","iopub.execute_input":"2026-09-24T12:57:18.156379Z","iopub.status.idle":"2026-09-24T12:57:18.181112Z","shell.execute_reply.started":"2026-09-24T12:57:18.156347Z","shell.execute_reply":"2026-09-24T12:57:18.180298Z"}},"outputs":[{"name":"stdout","text":"Kontrast wpisany w nagłówku: 97 / 1252\n\nTR < 10 ms (podejrzenie CE-MRA): 13\nTR >= 10 ms (podejrzenie TOF):   1082\n\nNajczęstsze opisy serii:\nSeriesDescription\nMRA                         156\nTOF_3D_MULTI-SLAB           109\nAX COW                       78\nTOF_FL3D_TRA                 59\nAX TOF COW                   42\nAX TOF COW 0.6               41\nAX 3D TOF BRAIN              31\nAX 3DTOF                     26\nH-AX 3D TOF-COW              25\nBRAIN AX 3D MRA_TOF          23\nTOF_3D_MULTI-SLAB_HEAD       22\nTOF_3D_5X_MULTI-SLAB 384     17\nTOF_CS_ACC5.0                17\n3D TOF MRA NEW               17\nCOW                          15\nAX 3DTOF 3SLAB               13\nAX 3D TOF                    13\nAX TOF 3D - MRA COW          13\nAX 3DTOF 2SLAB               12\nAX 3D TOF SLAB               12\nName: count, dtype: int64\n\nProducenci:\nManufacturer\nSIEMENS                    381\nGE MEDICAL SYSTEMS         298\nSiemens Healthineers       231\nPhilips Medical Systems    179\nTOSHIBA                     48\nSiemens                     45\nPhilips                     30\nCANON_MEC                   17\nPhilips Healthcare          15\nTOSHIBA_MEC                  5\nSiemens HealthCare GmbH      2\nHitachi, Ltd.                1\nName: count, dtype: int64\n","output_type":"stream"}],"execution_count":6},{"cell_type":"code","source":"# 1. Ujednolicenie producentów\ndef vendor(m):\n    m = str(m).upper()\n    for key, name in [(\"SIEMENS\", \"Siemens\"), (\"GE\", \"GE\"), (\"PHILIPS\", \"Philips\"),\n                      (\"TOSHIBA\", \"Canon/Toshiba\"), (\"CANON\", \"Canon/Toshiba\"), (\"HITACHI\", \"Hitachi\")]:\n        if key in m:\n            return name\n    return \"Inny/brak\"\n\nmra_meta[\"Vendor\"] = mra_meta[\"Manufacturer\"].apply(vendor)\nmra_meta[\"contrast\"] = mra_meta[\"ContrastBolusAgent\"].notna()\nprint(mra_meta[\"Vendor\"].value_counts(), \"\\n\")\n\n# 2. Te 157 serii bez TR - co to jest?\nno_tr = mra_meta[mra_meta[\"RepetitionTime\"].isna()]\nprint(\"Serie bez TR:\", len(no_tr))\nif \"error\" in mra_meta:\n    print(\"  w tym błędy odczytu:\", no_tr[\"error\"].notna().sum())\nprint(no_tr[[\"SeriesDescription\", \"Vendor\", \"n_files\"]].value_counts().head(15), \"\\n\")\n\n# 3. Kontrast w nagłówku vs TR\nmra_meta[\"TR_grupa\"] = pd.cut(mra_meta[\"RepetitionTime\"], [0, 10, 1000], labels=[\"TR<10\", \"TR>=10\"])\nprint(pd.crosstab(mra_meta[\"TR_grupa\"].astype(str), mra_meta[\"contrast\"], margins=True), \"\\n\")\n\n# 4. Szczegóły serii z kontrastem\ncols = [\"SeriesDescription\", \"ContrastBolusAgent\", \"RepetitionTime\", \"EchoTime\", \"FlipAngle\", \"Vendor\"]\nprint(mra_meta.loc[mra_meta[\"contrast\"], cols].to_string(max_rows=40))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T12:58:27.617051Z","iopub.execute_input":"2026-09-24T12:58:27.618146Z","iopub.status.idle":"2026-09-24T12:58:27.69409Z","shell.execute_reply.started":"2026-09-24T12:58:27.618096Z","shell.execute_reply":"2026-09-24T12:58:27.693176Z"}},"outputs":[{"name":"stdout","text":"Vendor\nSiemens          659\nGE               298\nPhilips          224\nCanon/Toshiba     70\nHitachi            1\nName: count, dtype: int64 \n\nSerie bez TR: 157\nSeriesDescription  Vendor   n_files\nMRA                Philips  1          156\nT2 TSE             Philips  1            1\nName: count, dtype: int64 \n\ncontrast  False  True   All\nTR_grupa                   \nTR<10         5     8    13\nTR>=10      993    89  1082\nnan         157     0   157\nAll        1155    97  1252 \n\n                           SeriesDescription ContrastBolusAgent  RepetitionTime  EchoTime  FlipAngle   Vendor\n18                        TOF_FL3D_TRA_CS7.2                             20.350     3.690       20.0  Siemens\n50                        AX 3D TOF COW POST           PROHANCE          21.000     3.420       20.0  Siemens\n64    AX COW POST NO SUPERIOR SATBAND PER UR     17 ML PROHANCE          22.000     3.600       18.0  Siemens\n96                       Brain Ax 3D MRA_TOF                             43.940     2.880       22.0  Siemens\n101                              AX COW POST     12 ML PROHANCE          22.000     3.600       18.0  Siemens\n104                           AX 3D TOF POST     20 ML PROHANCE          22.000     3.430       18.0  Siemens\n111                       AX 3D TOF COW POST           PROHANCE          21.000     3.420       20.0  Siemens\n138                              AX COW POST   19 ML MULTIHANCE          22.000     3.600       18.0  Siemens\n144                            TOF_CS_ACC5.0                             23.290     7.120       20.0  Siemens\n165                               cemra post         Multihance           3.420     1.230       23.0  Siemens\n176                       AX 3D TOF COW POST           PROHANCE          21.000     3.420       18.0  Siemens\n177                              AX COW POST     20 ML PROHANCE          22.000     3.600       18.0  Siemens\n189                                AX 3D COW                             23.000     3.455       18.0  Philips\n204                            h-Ax.s3DI_COW                             23.000     3.455       18.0  Philips\n207   AX COW POST NO SUPERIOR SATBAND PER UR     18 ML PROHANCE          22.000     3.600       18.0  Siemens\n231                            TOF_CS_ACC5.0                             23.290     7.120       20.0  Siemens\n258                              Ax.s3DI_COW                             23.000     3.455       18.0  Philips\n284   AX COW POST NO SUPERIOR SATBAND PER UR   12 mL MULTIHANCE          22.000     3.600       18.0  Siemens\n308                            TOF_CS_ACC5.0                             23.290     7.120       20.0  Siemens\n333                       AX 3D TOF COW POST           PROHANCE          21.000     3.420       20.0  Siemens\n...                                      ...                ...             ...       ...        ...      ...\n1032                               AX 3D COW                             23.000     3.455       18.0  Philips\n1038                      TOF_CS_WHOLE_BRAIN                             23.370     7.120       20.0  Siemens\n1039                           TOF_CS_ACC5.0                             23.290     7.120       20.0  Siemens\n1065            3D Ax TOF SPGR FS HyperSense             YES 10          22.000     2.500       15.0       GE\n1076                              cemra post         multihance           3.420     1.230       23.0  Siemens\n1078                          AX COW POST WB   21 ML MULTIHANCE          22.000     3.600       18.0  Siemens\n1101                          AX 3D TOF POST      10ML PROHANCE          21.000     3.430       18.0  Siemens\n1130                             Ax.s3DI_COW                             23.000     3.455       18.0  Philips\n1138                      TOF_FL3D_TRA_CS7.2                             20.350     3.690       20.0  Siemens\n1140                           TOF_CS_ACC5.0                             23.290     7.120       20.0  Siemens\n1148                           TOF_CS_ACC5.0                             23.290     7.120       20.0  Siemens\n1159                          TOF_MRA_3D_AX_                             25.000     7.000       25.0  Siemens\n1169                      AX 3D TOF COW POST           PROHANCE          21.000     3.420       20.0  Siemens\n1187                             AX COW POST     10 ML PROHANCE          29.000     7.000       25.0  Siemens\n1188                          TOF_MRA_3D_AX_                             25.000     7.000       25.0  Siemens\n1194                      AX 3D TOF COW POST           PROHANCE          21.000     3.420       20.0  Siemens\n1207                           TOF_CS_ACC5.0                             23.290     7.120       20.0  Siemens\n1229              tof_fl3d_tra fast 2019 new           PROHANCE          21.000     3.420       20.0  Siemens\n1237                            tof_fl3d_tra           PROHANCE          21.000     3.420       20.0  Siemens\n1243                                    None       DeIdentified           4.056     1.468       25.0       GE\n","output_type":"stream"}],"execution_count":7},{"cell_type":"code","source":"# A. Poprawiona flaga kontrastu: tylko niepusty tekst\nagent = mra_meta[\"ContrastBolusAgent\"].fillna(\"\").str.strip()\nmra_meta[\"contrast\"] = agent != \"\"\nmra_meta[\"post_w_opisie\"] = mra_meta[\"SeriesDescription\"].fillna(\"\").str.upper().str.contains(\"POST\")\nprint(pd.crosstab(mra_meta[\"TR_grupa\"].astype(str),\n                  [mra_meta[\"contrast\"], mra_meta[\"post_w_opisie\"]], margins=True), \"\\n\")\n\n# B. Wszystkie 13 serii z TR < 10 ms\ncols = [\"SeriesDescription\", \"ContrastBolusAgent\", \"RepetitionTime\", \"EchoTime\", \"FlipAngle\", \"Vendor\", \"n_files\"]\nprint(mra_meta.loc[mra_meta[\"RepetitionTime\"] < 10, cols].to_string(), \"\\n\")\n\n# C. Zajrzyjmy do jednej serii Philips z 1 plikiem\nuid = mra_meta.loc[mra_meta[\"RepetitionTime\"].isna() & (mra_meta[\"SeriesDescription\"] == \"MRA\"), \"uid\"].iloc[0]\nfolder = f\"{BASE}/series/{uid}\"\nds = pydicom.dcmread(f\"{folder}/{os.listdir(folder)[0]}\", stop_before_pixels=True)\nprint(\"SOPClassUID:\", ds.get(\"SOPClassUID\"))\nprint(\"NumberOfFrames:\", ds.get(\"NumberOfFrames\"))\nprint(\"ImageType:\", ds.get(\"ImageType\"))\nprint(\"Rows x Columns:\", ds.get(\"Rows\"), \"x\", ds.get(\"Columns\"))\nprint(\"Ma SharedFunctionalGroups:\", \"SharedFunctionalGroupsSequence\" in ds)\nprint(\"Ma PerFrameFunctionalGroups:\", \"PerFrameFunctionalGroupsSequence\" in ds)\nprint(\"\\nWszystkie tagi najwyższego poziomu:\")\nprint([e.keyword or str(e.tag) for e in ds])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T12:58:32.637095Z","iopub.execute_input":"2026-09-24T12:58:32.638153Z","iopub.status.idle":"2026-09-24T12:58:32.698961Z","shell.execute_reply.started":"2026-09-24T12:58:32.638116Z","shell.execute_reply":"2026-09-24T12:58:32.69808Z"}},"outputs":[{"name":"stdout","text":"contrast      False       True        All\npost_w_opisie False True False True      \nTR_grupa                                 \nTR<10             5    0     2    6    13\nTR>=10         1041    1    11   29  1082\nnan             157    0     0    0   157\nAll            1203    1    13   35  1252 \n\n              SeriesDescription ContrastBolusAgent  RepetitionTime  EchoTime  FlipAngle   Vendor  n_files\n42                    cemra pre               None          3.4200     1.230       23.0  Siemens      104\n165                  cemra post         Multihance          3.4200     1.230       23.0  Siemens      120\n245                        None               None          5.4798     1.872       35.0  Philips      160\n473         cemra post_TTC=1.0s         MULTIHANCE          3.4700     1.330       17.0  Siemens      104\n478          cemra pre_TTC=1.0s               None          3.4700     1.330       17.0  Siemens       96\n479         cemra post_TTC=1.0s         MULTIHANCE          3.4700     1.330       17.0  Siemens       96\n647                        None               None          3.8560     1.396       25.0       GE      248\n773                        None       DeIdentified          3.2000     1.120       25.0  Siemens       72\n872         cemra post_TTC=1.0s         MULTIHANCE          3.4700     1.330       17.0  Siemens       96\n897                  cemra post         multihance          3.4200     1.230       23.0  Siemens      104\n1076                 cemra post         multihance          3.4200     1.230       23.0  Siemens      104\n1222  Ph1/Cor ceMRA FT elliptic               None          5.0360     1.776       25.0       GE      116\n1243                       None       DeIdentified          4.0560     1.468       25.0       GE      100 \n\nSOPClassUID: 1.2.840.10008.5.1.4.1.1.4.1\nNumberOfFrames: 150\nImageType: ['ORIGINAL', 'PRIMARY', 'TOF', 'NONE']\nRows x Columns: 528 x 528\nMa SharedFunctionalGroups: True\nMa PerFrameFunctionalGroups: True\n\nWszystkie tagi najwyższego poziomu:\n['SpecificCharacterSet', 'ImageType', 'SOPClassUID', 'SOPInstanceUID', 'StudyDate', 'SeriesDate', 'ContentDate', 'AcquisitionDateTime', 'StudyTime', 'SeriesTime', 'ContentTime', 'AccessionNumber', 'Modality', 'Manufacturer', 'StudyDescription', 'SeriesDescription', 'ManufacturerModelName', 'PatientName', 'PatientID', 'PatientSex', 'PatientWeight', 'PatientIdentityRemoved', 'DeidentificationMethod', 'DeidentificationMethodCodeSequence', '(0013,0010)', '(0013,1001)', '(0013,1003)', 'BodyPartExamined', 'MRAcquisitionType', 'MagneticFieldStrength', 'SpacingBetweenSlices', 'PixelBandwidth', 'SoftwareVersions', 'PatientPosition', 'ContentQualification', 'PulseSequenceName', 'EchoPulseSequence', 'MultiPlanarExcitation', 'PhaseContrast', 'TimeOfFlightContrast', 'Spoiling', 'SteadyStatePulseSequence', 'EchoPlanarPulseSequence', 'MagnetizationTransfer', 'T2Preparation', 'BloodSignalNulling', 'SaturationRecovery', 'SpectrallySelectedSuppression', 'SpectrallySelectedExcitation', 'SpatialPresaturation', 'Tagging', 'OversamplingPhase', 'GeometryOfKSpaceTraversal', 'SegmentedKSpaceTraversal', 'RectilinearPhaseEncodeReordering', 'TagThickness', 'PartialFourierDirection', 'CardiacSynchronizationTechnique', 'TransmitCoilType', 'ChemicalShiftReference', 'MRAcquisitionFrequencyEncodingSteps', 'Decoupling', 'KSpaceFiltering', 'ParallelReductionFactorInPlane', 'AcquisitionDuration', 'ParallelAcquisition', 'ParallelAcquisitionTechnique', 'PartialFourier', 'VelocityEncodingDirection', 'VelocityEncodingMinimumValue', 'NumberOfKSpaceTrajectories', 'CoverageOfKSpace', 'ResonantNucleus', 'FrequencyCorrection', 'ParallelReductionFactorOutOfPlane', 'ParallelReductionFactorSecondInPlane', 'RespiratoryMotionCompensationTechnique', 'BulkMotionCompensationTechnique', 'ApplicableSafetyStandardAgency', 'SpecificAbsorptionRateDefinition', 'GradientOutputType', 'SpecificAbsorptionRateValue', 'GradientOutput', 'FlowCompensationDirection', 'WaterReferencedPhaseCorrection', 'MRSpectroscopyAcquisitionType', 'MRAcquisitionPhaseEncodingStepsInPlane', 'RFEchoTrainLength', 'GradientEchoTrainLength', 'StudyInstanceUID', 'SeriesInstanceUID', 'StudyID', 'SeriesNumber', 'AcquisitionNumber', 'InstanceNumber', 'FrameOfReferenceUID', 'PositionReferenceIndicator', 'FrameLaterality', 'DimensionOrganizationSequence', 'DimensionIndexSequence', 'RespiratoryIntervalTime', 'NominalRespiratoryTriggerDelayTime', 'SamplesPerPixel', 'PhotometricInterpretation', 'NumberOfFrames', 'Rows', 'Columns', 'BitsAllocated', 'BitsStored', 'HighBit', 'PixelRepresentation', 'BurnedInAnnotation', 'LossyImageCompression', 'LUTExplanation', 'DataPointRows', 'DataPointColumns', 'SharedFunctionalGroupsSequence', 'PerFrameFunctionalGroupsSequence']\n","output_type":"stream"}],"execution_count":8},{"cell_type":"code","source":"# Sprawdzenie ImageType dla 157 serii bez TR\ndef image_type(uid):\n    folder = f\"{BASE}/series/{uid}\"\n    ds = pydicom.dcmread(f\"{folder}/{os.listdir(folder)[0]}\", stop_before_pixels=True)\n    return \"\\\\\".join(ds.get(\"ImageType\", []))\n\nbez_tr = mra_meta[\"RepetitionTime\"].isna()\nmra_meta.loc[bez_tr, \"ImageType\"] = mra_meta.loc[bez_tr, \"uid\"].apply(image_type)\nprint(mra_meta.loc[bez_tr, [\"SeriesDescription\", \"ImageType\"]].value_counts(), \"\\n\")\n\n# Ostateczna klasyfikacja\ndef klasa(r):\n    opis = str(r[\"SeriesDescription\"]).upper()\n    if \"T2\" in opis and \"TSE\" in opis:\n        return \"błąd etykiety (T2)\"\n    if r[\"RepetitionTime\"] < 10:\n        return \"CE-MRA\"\n    if r[\"contrast\"] or r[\"post_w_opisie\"]:\n        return \"TOF po kontraście\"\n    if r[\"RepetitionTime\"] >= 10 or \"TOF\" in str(r.get(\"ImageType\", \"\")):\n        return \"TOF\"\n    return \"nieustalone\"\n\nmra_meta[\"klasa\"] = mra_meta.apply(klasa, axis=1)\nprint(mra_meta[\"klasa\"].value_counts(), \"\\n\")\n\n# Pole magnetyczne i producent per klasa\nmra_meta[\"Tesla\"] = pd.to_numeric(mra_meta[\"MagneticFieldStrength\"], errors=\"coerce\")\nmra_meta.loc[mra_meta[\"Tesla\"] > 100, \"Tesla\"] /= 10000   # Gauss -> Tesla\nprint(pd.crosstab(mra_meta[\"Tesla\"].round(1), mra_meta[\"klasa\"], margins=True), \"\\n\")\nprint(pd.crosstab(mra_meta[\"Vendor\"], mra_meta[\"klasa\"], margins=True))\n\nmra_meta.to_csv(\"mra_meta_klasy.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T13:02:43.287248Z","iopub.execute_input":"2026-09-24T13:02:43.287687Z","iopub.status.idle":"2026-09-24T13:02:44.056209Z","shell.execute_reply.started":"2026-09-24T13:02:43.287655Z","shell.execute_reply":"2026-09-24T13:02:44.055332Z"}},"outputs":[{"name":"stdout","text":"SeriesDescription  ImageType                \nMRA                ORIGINAL\\PRIMARY\\TOF\\NONE    156\nT2 TSE             ORIGINAL\\PRIMARY\\T2\\NONE       1\nName: count, dtype: int64 \n\nklasa\nTOF                   1197\nTOF po kontraście       41\nCE-MRA                  13\nbłąd etykiety (T2)       1\nName: count, dtype: int64 \n\nklasa  CE-MRA   TOF  TOF po kontraście  błąd etykiety (T2)   All\nTesla                                                           \n1.2         0     1                  0                   0     1\n1.5         8   694                  5                   1   708\n3.0         5   497                 36                   0   538\nAll        13  1192                 41                   1  1247 \n\nklasa          CE-MRA   TOF  TOF po kontraście  błąd etykiety (T2)   All\nVendor                                                                  \nCanon/Toshiba       0    70                  0                   0    70\nGE                  3   289                  6                   0   298\nHitachi             0     1                  0                   0     1\nPhilips             1   222                  0                   1   224\nSiemens             9   615                 35                   0   659\nAll                13  1197                 41                   1  1252\n","output_type":"stream"}],"execution_count":9},{"cell_type":"code","source":"import numpy as np\n\nTAGS_MR = [\"SeriesDescription\", \"ContrastBolusAgent\", \"RepetitionTime\", \"EchoTime\",\n           \"InversionTime\", \"FlipAngle\", \"ImageType\", \"ScanningSequence\",\n           \"MRAcquisitionType\", \"Manufacturer\", \"MagneticFieldStrength\",\n           \"SliceThickness\", \"ImageOrientationPatient\"]\n\ndef plaszczyzna(iop):\n    try:\n        v = np.array([float(x) for x in iop])\n        n = np.abs(np.cross(v[:3], v[3:]))\n        return [\"strzałkowa\", \"czołowa\", \"osiowa\"][int(np.argmax(n))]\n    except Exception:\n        return None\n\nrows = []\nmr_uids = train.loc[train[\"Modality\"].isin([\"MRI T2\", \"MRI T1post\"]), [\"SeriesInstanceUID\", \"Modality\"]]\nfor uid, mod in tqdm(mr_uids.values):\n    folder = f\"{BASE}/series/{uid}\"\n    try:\n        files = os.listdir(folder)\n        ds = pydicom.dcmread(f\"{folder}/{sorted(files)[0]}\", stop_before_pixels=True)\n        row = {\"uid\": uid, \"etykieta\": mod, \"n_files\": len(files)}\n        for t in TAGS_MR:\n            v = get_tag(ds, t)\n            if t == \"ImageOrientationPatient\":\n                row[\"plaszczyzna\"] = plaszczyzna(v) if v is not None else None\n            elif t in (\"ImageType\", \"ScanningSequence\") and v is not None and not isinstance(v, str):\n                row[t] = \"\\\\\".join(str(x) for x in v)\n            else:\n                row[t] = str(v) if v is not None else None\n        rows.append(row)\n    except Exception as e:\n        rows.append({\"uid\": uid, \"etykieta\": mod, \"error\": str(e)})\n\nmr_meta = pd.DataFrame(rows)\nfor c in [\"RepetitionTime\", \"EchoTime\", \"InversionTime\", \"FlipAngle\", \"SliceThickness\"]:\n    mr_meta[c] = pd.to_numeric(mr_meta[c], errors=\"coerce\")\nmr_meta[\"contrast\"] = mr_meta[\"ContrastBolusAgent\"].fillna(\"\").str.strip() != \"\"\nmr_meta[\"Vendor\"] = mr_meta[\"Manufacturer\"].apply(vendor)\nmr_meta.to_csv(\"mr_meta.csv\", index=False)\n\n# Podsumowanie\nprint(\"Błędy odczytu:\", mr_meta.get(\"error\", pd.Series(dtype=str)).notna().sum(), \"\\n\")\nprint(\"Kontrast w nagłówku:\\n\", pd.crosstab(mr_meta[\"etykieta\"], mr_meta[\"contrast\"]), \"\\n\")\nprint(\"Płaszczyzna:\\n\", pd.crosstab(mr_meta[\"plaszczyzna\"].fillna(\"brak\"), mr_meta[\"etykieta\"]), \"\\n\")\nprint(\"2D/3D:\\n\", pd.crosstab(mr_meta[\"MRAcquisitionType\"].fillna(\"brak\"), mr_meta[\"etykieta\"]), \"\\n\")\nprint(\"Parametry (mediana i zakres):\")\nprint(mr_meta.groupby(\"etykieta\")[[\"RepetitionTime\", \"EchoTime\", \"InversionTime\", \"FlipAngle\", \"SliceThickness\"]]\n      .describe().T.loc[(slice(None), [\"count\", \"min\", \"50%\", \"max\"]), :].round(1), \"\\n\")\nfor lab in [\"MRI T2\", \"MRI T1post\"]:\n    print(f\"--- Najczęstsze opisy: {lab} ---\")\n    print(mr_meta.loc[mr_meta[\"etykieta\"] == lab, \"SeriesDescription\"].str.upper().value_counts().head(20), \"\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T13:04:02.935533Z","iopub.execute_input":"2026-09-24T13:04:02.935922Z","iopub.status.idle":"2026-09-24T13:05:05.143611Z","shell.execute_reply.started":"2026-09-24T13:04:02.935891Z","shell.execute_reply":"2026-09-24T13:05:05.142615Z"}},"outputs":[{"name":"stderr","text":"  0%|          | 2/1288 [00:00<01:35, 13.52it/s]/usr/local/lib/python3.12/dist-packages/pydicom/valuerep.py:440: UserWarning: The value length (20) exceeds the maximum length of 16 allowed for VR SH.\n  warn_and_log(msg)\n  1%|          | 12/1288 [00:00<00:50, 25.35it/s]/usr/local/lib/python3.12/dist-packages/pydicom/valuerep.py:440: UserWarning: The value length (64) exceeds the maximum length of 16 allowed for VR SH.\n  warn_and_log(msg)\n100%|██████████| 1288/1288 [01:02<00:00, 20.74it/s]","output_type":"stream"},{"name":"stdout","text":"Błędy odczytu: 0 \n\nKontrast w nagłówku:\n contrast    False  True \netykieta                \nMRI T1post    108    197\nMRI T2        944     39 \n\nPłaszczyzna:\n etykieta     MRI T1post  MRI T2\nplaszczyzna                    \nczołowa              27      43\nosiowa              258     903\nstrzałkowa           20      37 \n\n2D/3D:\n etykieta           MRI T1post  MRI T2\nMRAcquisitionType                    \n2D                         85     937\n3D                        220      46 \n\nParametry (mediana i zakres):\netykieta              MRI T1post   MRI T2\nRepetitionTime count       305.0    818.0\n               min           5.2      5.7\n               50%         500.0   4820.0\n               max        3100.1  14535.6\nEchoTime       count       305.0    818.0\n               min           1.8      2.3\n               50%           3.2    100.0\n               max          44.0    382.0\nInversionTime  count       160.0     52.0\n               min           0.0      0.0\n               50%         900.0      0.0\n               max        1260.0   2500.0\nFlipAngle      count       305.0    818.0\n               min           8.0     15.0\n               50%          12.0    142.0\n               max         160.0    180.0\nSliceThickness count       305.0    983.0\n               min           0.8      0.5\n               50%           1.2      5.0\n               max           5.0      6.0 \n\n--- Najczęstsze opisy: MRI T2 ---\nSeriesDescription\nT2 TSE             164\nAX T2               92\nAX T2 PROPELLER     68\nT2 AX FS            54\nAX T2 PROP          36\nT2 FS AX            35\nAX T2 FRFSE         32\nAX T2 TSE BLADE     31\nSAG T2              28\nCORONAL T2          24\nT2_TSE_TRA-FS       23\nT2_TSE_AX           20\nT2_TSE_FS_TRA       19\nAX T2 FSE           18\nT2W_TSE             16\nAX T2 +C            15\nT2 AX FS BLADE      15\nT2_TSE_TRA_FS       14\nAX T2 FS            12\nBRAIN AX T2_SE      10\nName: count, dtype: int64 \n\n--- Najczęstsze opisy: MRI T1post ---\nSeriesDescription\n3D AX T1 BRAVO+GD             20\nT1FS_3D_AX WHOLE BRAIN_GD.    20\nAX T1 +C                      18\nGAD BRAIN AX 3D T1_GRE        13\nAX T1 MPR POST                 8\nAX FSPGR 3D                    8\n3D AX T1 BRAVO+GD FOR SRT      7\nVOLUMETRIC AXIAL GD            6\nST1W_3D_HR                     6\nT1 FLAIR AX FS GD              5\nAX T1 FLAIR +C                 4\nAX SPGR +C                     4\nCOR T1 +C                      4\nCOR OBL SPGR +C                4\nT1 FS AX GD                    4\nVOLUMETRIC GD                  4\n<MPR POST AX                   4\n3D AX T1 BRAVO+C               4\nCOR 3DSPGR +C                  4\nT1_SPC_SAG_ISO_MTC+C           4\nName: count, dtype: int64 \n\n","output_type":"stream"},{"name":"stderr","text":"\n","output_type":"stream"}],"execution_count":10},{"cell_type":"code","source":"def dowod_kontrastu(r):\n    opis = str(r[\"SeriesDescription\"]).upper()\n    slowa = [\"GD\", \"GAD\", \"+C\", \"C+\", \"POST\", \"CONTRAST\", \"HANCE\", \"DOTAREM\", \"GADAVIST\"]\n    if r[\"contrast\"]:\n        return \"tag w nagłówku\"\n    if any(s in opis for s in slowa):\n        return \"tylko opis\"\n    return \"brak dowodu\"\n\ndef podtyp_t1(r):\n    tr, ti = r[\"RepetitionTime\"], r[\"InversionTime\"] if pd.notna(r[\"InversionTime\"]) else 0\n    d3 = r[\"MRAcquisitionType\"] == \"3D\"\n    if d3 and tr < 100:\n        return \"3D GRE z inwersją (BRAVO/MPRAGE-typ)\" if ti > 0 else \"3D GRE (SPGR/VIBE)\"\n    if d3 and ti > 0:\n        return \"3D MPRAGE (Siemens)\"\n    if d3:\n        return \"3D TSE (SPACE/CUBE)\"\n    if ti > 0:\n        return \"2D T1 FLAIR\"\n    return \"2D SE/TSE\"\n\ndef podtyp_t2(r):\n    tr, ti = r[\"RepetitionTime\"], r[\"InversionTime\"] if pd.notna(r[\"InversionTime\"]) else 0\n    if pd.isna(tr):\n        return f\"brak TR (ImageType: {r['ImageType']})\"\n    if ti > 1500:\n        return \"FLAIR\"\n    if tr < 100:\n        return \"GRE (T2*/bSSFP)\"\n    if r[\"MRAcquisitionType\"] == \"3D\":\n        return \"3D T2 (SPACE/CUBE)\"\n    return \"2D T2 TSE/FSE\"\n\nmr_meta[\"kontrast_dowod\"] = mr_meta.apply(dowod_kontrastu, axis=1)\nt1 = mr_meta[\"etykieta\"] == \"MRI T1post\"\nt2 = mr_meta[\"etykieta\"] == \"MRI T2\"\nmr_meta.loc[t1, \"podtyp\"] = mr_meta[t1].apply(podtyp_t1, axis=1)\nmr_meta.loc[t2, \"podtyp\"] = mr_meta[t2].apply(podtyp_t2, axis=1)\n\nprint(\"=== T1post: dowód kontrastu ===\")\nprint(mr_meta.loc[t1, \"kontrast_dowod\"].value_counts(), \"\\n\")\nprint(\"=== T1post: podtyp ===\")\nprint(mr_meta.loc[t1, \"podtyp\"].value_counts(), \"\\n\")\nprint(\"=== T1post bez żadnego dowodu kontrastu - opisy ===\")\nprint(mr_meta.loc[t1 & (mr_meta[\"kontrast_dowod\"] == \"brak dowodu\"), \"SeriesDescription\"].value_counts().head(20), \"\\n\")\n\nprint(\"=== T2: podtyp ===\")\nprint(mr_meta.loc[t2, \"podtyp\"].value_counts(), \"\\n\")\nprint(\"=== T2: kontrast ===\")\nprint(mr_meta.loc[t2, \"kontrast_dowod\"].value_counts(), \"\\n\")\nprint(\"=== T2 GRE i FLAIR - opisy ===\")\nprint(mr_meta.loc[t2 & mr_meta[\"podtyp\"].isin([\"GRE (T2*/bSSFP)\", \"FLAIR\"]),\n                  [\"podtyp\", \"SeriesDescription\", \"RepetitionTime\", \"EchoTime\", \"FlipAngle\"]].to_string(max_rows=40), \"\\n\")\n\n# Pole i producent\nmr_meta[\"Tesla\"] = pd.to_numeric(mr_meta[\"MagneticFieldStrength\"], errors=\"coerce\")\nmr_meta.loc[mr_meta[\"Tesla\"] > 100, \"Tesla\"] /= 10000\nprint(pd.crosstab(mr_meta[\"Tesla\"].round(1).fillna(-1), mr_meta[\"etykieta\"], margins=True), \"\\n\")\nprint(pd.crosstab(mr_meta[\"Vendor\"], mr_meta[\"etykieta\"], margins=True))\n\nmr_meta.to_csv(\"mr_meta_klasy.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-24T13:07:15.071608Z","iopub.execute_input":"2026-09-24T13:07:15.072591Z","iopub.status.idle":"2026-09-24T13:07:15.191655Z","shell.execute_reply.started":"2026-09-24T13:07:15.072555Z","shell.execute_reply":"2026-09-24T13:07:15.19081Z"}},"outputs":[{"name":"stdout","text":"=== T1post: dowód kontrastu ===\nkontrast_dowod\ntag w nagłówku    197\ntylko opis         77\nbrak dowodu        31\nName: count, dtype: int64 \n\n=== T1post: podtyp ===\npodtyp\n3D MPRAGE (Siemens)                     73\n2D SE/TSE                               67\n3D GRE (SPGR/VIBE)                      66\n3D GRE z inwersją (BRAVO/MPRAGE-typ)    64\n2D T1 FLAIR                             18\n3D TSE (SPACE/CUBE)                     17\nName: count, dtype: int64 \n\n=== T1post bez żadnego dowodu kontrastu - opisy ===\nSeriesDescription\nsT1W_3D_HR                 6\nAX CUBE T1 REFORMAT        1\nt1_space_sag_caipi4_iso    1\nT1 ax IR_FS                1\nAx T1 SE                   1\n(C) Sag  3D T1 BRAVO       1\n3D T1 SAG                  1\nName: count, dtype: int64 \n\n=== T2: podtyp ===\npodtyp\n2D T2 TSE/FSE                                     772\nbrak TR (ImageType: ORIGINAL\\PRIMARY\\T2\\NONE)     164\n3D T2 (SPACE/CUBE)                                 34\nGRE (T2*/bSSFP)                                    11\nFLAIR                                               1\nbrak TR (ImageType: ORIGINAL\\PRIMARY\\TOF\\NONE)      1\nName: count, dtype: int64 \n\n=== T2: kontrast ===\nkontrast_dowod\nbrak dowodu       939\ntag w nagłówku     39\ntylko opis          5\nName: count, dtype: int64 \n\n=== T2 GRE i FLAIR - opisy ===\n               podtyp  SeriesDescription  RepetitionTime  EchoTime  FlipAngle\n121   GRE (T2*/bSSFP)    t2_ci3d_tra_iso          6.1500     2.730       70.0\n166   GRE (T2*/bSSFP)     ciss3d_tra_iso          8.2600     3.710       50.0\n174   GRE (T2*/bSSFP)            AX CISS          6.8100     3.410       80.0\n328   GRE (T2*/bSSFP)    t2_ci3d_tra_iso          6.0700     2.640       70.0\n377   GRE (T2*/bSSFP)     ciss3d_tra_iso          5.7800     2.460       50.0\n486   GRE (T2*/bSSFP)     Ax 3D FIESTA-C          5.7240     2.284       55.0\n520             FLAIR           ax flair       9000.0000   137.000      150.0\n745   GRE (T2*/bSSFP)    t2_ci3d_tra_iso          6.0700     2.640       70.0\n859   GRE (T2*/bSSFP)     Ax 3D FIESTA-C          5.8640     2.360       55.0\n1032  GRE (T2*/bSSFP)     Ax 3D FIESTA-C          6.7920     2.488       55.0\n1071  GRE (T2*/bSSFP)  TRIGEM 150 slices          6.1714     3.086       60.0\n1218  GRE (T2*/bSSFP)               None          7.3600     2.724       60.0 \n\netykieta  MRI T1post  MRI T2   All\nTesla                             \n-1.0               0       4     4\n 1.2               1       2     3\n 1.5             159     654   813\n 3.0             145     323   468\n All             305     983  1288 \n\netykieta       MRI T1post  MRI T2   All\nVendor                                 \nCanon/Toshiba           0      16    16\nGE                    124     295   419\nHitachi                 1       2     3\nPhilips                28     254   282\nSiemens               152     416   568\nAll                   305     983  1288\n","output_type":"stream"}],"execution_count":11},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}