From 9328c0218a9bdec9a82603340a994b03bc3fc9f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20F=C3=B6rster?= Date: Wed, 7 Aug 2024 20:06:06 +0200 Subject: [PATCH] improved imports, dummy dataset generation --- .../Analyse_5-1_Timeline.ipynb | 0 .../Preprocess_Pipeline.ipynb | 0 .../Token_Analysis.ipynb | 0 .../archive/Analyse.ipynb | 0 .../archive/Analyse_2-2.ipynb | 0 .../archive/Analyse_2.ipynb | 0 .../archive/Analyse_3.ipynb | 0 .../archive/Analyse_4-1.ipynb | 0 .../archive/Analyse_4-2.ipynb | 0 .../archive/test_new_dupl_merge.ipynb | 0 .../archive/test_sentence_trf.ipynb | 0 .../display_results.ipynb | 0 notebooks/dummy_data_generation.ipynb | 1244 +++++++++++++++++ {test-notebooks => notebooks}/lang_main.xml | 0 .../lang_main_config.toml | 0 {test-notebooks => notebooks}/misc.ipynb | 336 ++++- .../styles_template.xml | 0 .../timeline_analysis.ipynb | 0 .../truncate_dataset.ipynb | 0 pdm.lock | 114 +- pyproject.toml | 2 + scripts/dash_timeline_static.py | 140 +- scripts/lang_main_config.toml | 18 +- src/lang_main/__init__.py | 2 +- src/lang_main/analysis/preprocessing.py | 5 +- src/lang_main/analysis/timeline.py | 9 +- src/lang_main/analysis/tokens.py | 20 +- src/lang_main/constants.py | 47 +- .../cytoscape_config/template_test.cys | Bin 33608 -> 33334 bytes src/lang_main/io.py | 3 +- src/lang_main/model_loader.py | 53 + src/lang_main/pipelines/predefined.py | 22 +- src/lang_main/render/cytoscape.py | 26 +- src/lang_main/types.py | 31 +- test-notebooks/image.png | Bin 54002 -> 0 bytes 35 files changed, 1966 insertions(+), 106 deletions(-) rename {test-notebooks => notebooks}/Analyse_5-1_Timeline.ipynb (100%) rename {test-notebooks => notebooks}/Preprocess_Pipeline.ipynb (100%) rename {test-notebooks => notebooks}/Token_Analysis.ipynb (100%) rename {test-notebooks => notebooks}/archive/Analyse.ipynb (100%) rename {test-notebooks => notebooks}/archive/Analyse_2-2.ipynb (100%) rename {test-notebooks => notebooks}/archive/Analyse_2.ipynb (100%) rename {test-notebooks => notebooks}/archive/Analyse_3.ipynb (100%) rename {test-notebooks => notebooks}/archive/Analyse_4-1.ipynb (100%) rename {test-notebooks => notebooks}/archive/Analyse_4-2.ipynb (100%) rename {test-notebooks => notebooks}/archive/test_new_dupl_merge.ipynb (100%) rename {test-notebooks => notebooks}/archive/test_sentence_trf.ipynb (100%) rename {test-notebooks => notebooks}/display_results.ipynb (100%) create mode 100644 notebooks/dummy_data_generation.ipynb rename {test-notebooks => notebooks}/lang_main.xml (100%) rename {test-notebooks => notebooks}/lang_main_config.toml (100%) rename {test-notebooks => notebooks}/misc.ipynb (97%) rename {test-notebooks => notebooks}/styles_template.xml (100%) rename {test-notebooks => notebooks}/timeline_analysis.ipynb (100%) rename {test-notebooks => notebooks}/truncate_dataset.ipynb (100%) create mode 100644 src/lang_main/model_loader.py delete mode 100644 test-notebooks/image.png diff --git a/test-notebooks/Analyse_5-1_Timeline.ipynb b/notebooks/Analyse_5-1_Timeline.ipynb similarity index 100% rename from test-notebooks/Analyse_5-1_Timeline.ipynb rename to notebooks/Analyse_5-1_Timeline.ipynb diff --git a/test-notebooks/Preprocess_Pipeline.ipynb b/notebooks/Preprocess_Pipeline.ipynb similarity index 100% rename from test-notebooks/Preprocess_Pipeline.ipynb rename to notebooks/Preprocess_Pipeline.ipynb diff --git a/test-notebooks/Token_Analysis.ipynb b/notebooks/Token_Analysis.ipynb similarity index 100% rename from test-notebooks/Token_Analysis.ipynb rename to notebooks/Token_Analysis.ipynb diff --git a/test-notebooks/archive/Analyse.ipynb b/notebooks/archive/Analyse.ipynb similarity index 100% rename from test-notebooks/archive/Analyse.ipynb rename to notebooks/archive/Analyse.ipynb diff --git a/test-notebooks/archive/Analyse_2-2.ipynb b/notebooks/archive/Analyse_2-2.ipynb similarity index 100% rename from test-notebooks/archive/Analyse_2-2.ipynb rename to notebooks/archive/Analyse_2-2.ipynb diff --git a/test-notebooks/archive/Analyse_2.ipynb b/notebooks/archive/Analyse_2.ipynb similarity index 100% rename from test-notebooks/archive/Analyse_2.ipynb rename to notebooks/archive/Analyse_2.ipynb diff --git a/test-notebooks/archive/Analyse_3.ipynb b/notebooks/archive/Analyse_3.ipynb similarity index 100% rename from test-notebooks/archive/Analyse_3.ipynb rename to notebooks/archive/Analyse_3.ipynb diff --git a/test-notebooks/archive/Analyse_4-1.ipynb b/notebooks/archive/Analyse_4-1.ipynb similarity index 100% rename from test-notebooks/archive/Analyse_4-1.ipynb rename to notebooks/archive/Analyse_4-1.ipynb diff --git a/test-notebooks/archive/Analyse_4-2.ipynb b/notebooks/archive/Analyse_4-2.ipynb similarity index 100% rename from test-notebooks/archive/Analyse_4-2.ipynb rename to notebooks/archive/Analyse_4-2.ipynb diff --git a/test-notebooks/archive/test_new_dupl_merge.ipynb b/notebooks/archive/test_new_dupl_merge.ipynb similarity index 100% rename from test-notebooks/archive/test_new_dupl_merge.ipynb rename to notebooks/archive/test_new_dupl_merge.ipynb diff --git a/test-notebooks/archive/test_sentence_trf.ipynb b/notebooks/archive/test_sentence_trf.ipynb similarity index 100% rename from test-notebooks/archive/test_sentence_trf.ipynb rename to notebooks/archive/test_sentence_trf.ipynb diff --git a/test-notebooks/display_results.ipynb b/notebooks/display_results.ipynb similarity index 100% rename from test-notebooks/display_results.ipynb rename to notebooks/display_results.ipynb diff --git a/notebooks/dummy_data_generation.ipynb b/notebooks/dummy_data_generation.ipynb new file mode 100644 index 0000000..4ebf4c7 --- /dev/null +++ b/notebooks/dummy_data_generation.ipynb @@ -0,0 +1,1244 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "91e5d121-4267-4ee7-baaa-3cec3da1f869", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "from collections import namedtuple\n", + "\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f7c989c6-67e1-4c34-bd08-34d6626cd33a", + "metadata": {}, + "outputs": [], + "source": [ + "N_SAMPLES = 1000\n", + "SEED = 42\n", + "RNG = np.random.default_rng(seed=SEED)\n", + "COLS_DUMMY_DATA = ['type', 'problem', 'action']\n", + "TOTAL_POSSIBILITY_FAILURES = 0.4\n", + "TYPE_MAPPING = {\n", + " 'Reguläre Wartung': 'Wartung',\n", + " 'Unerwarteter Fehler': 'Störungsmeldung',\n", + "}\n", + "OBJ_IDS_2_TXT = {\n", + " 1: 'Fräsmaschine-FS435X',\n", + " 2: 'Schleifmaschine-S4x87',\n", + " 3: 'Bohrbearbeitungszentrum-BBZ35',\n", + "}\n", + "STARTING_DATE = pd.to_datetime('2022-01-01')\n", + "ENDING_DATE = pd.to_datetime('2024-08-07')\n", + "DATASET_FEATURES = [\n", + " 'VorgangsID',\n", + " 'ObjektID',\n", + " 'HObjektText',\n", + " 'VorgangsTypName',\n", + " 'VorgangsBeschreibung',\n", + " 'ErledigungsBeschreibung',\n", + " 'ErstellungsDatum',\n", + " 'VorgangsDatum',\n", + " 'Arbeitsbeginn',\n", + " 'ErledigungsDatum',\n", + "]\n", + "DF_SKELLETON = {feat: [] for feat in DATASET_FEATURES}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "0be70014-4fe0-45dd-8bd5-f731bd12cfe1", + "metadata": {}, + "outputs": [], + "source": [ + "source = '../data/Dummy_Data.xlsx'\n", + "dest = f'../data/Dummy_Dataset_N_{N_SAMPLES}.csv'\n", + "pth_source = Path(source)\n", + "pth_dest = Path(dest)\n", + "assert pth_source.exists()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "193304e9-9db1-4697-ae48-836a716ce80e", + "metadata": {}, + "outputs": [], + "source": [ + "def read_dummy_data(pth_data, columns=COLS_DUMMY_DATA):\n", + " data = pd.read_excel(pth_data)\n", + " data.columns = columns.copy()\n", + " \n", + " return data" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "85ac2d6c-4eee-429a-8511-82f39d4e8716", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
typeproblemaction
0Reguläre WartungSchmierung der LagerNachfüllen des Schmiermittels
1Unerwarteter FehlerMotorüberhitzungAustausch des Kühlgebläses
2Reguläre WartungÜberprüfung der HydraulikReinigung und Nachfüllen der Hydraulikflüssigkeit
3Unerwarteter FehlerElektronikfehlerAustausch der defekten Platine
4Reguläre WartungKalibrierung der SensorenJustierung und Test der Sensoren
\n", + "
" + ], + "text/plain": [ + " type problem \\\n", + "0 Reguläre Wartung Schmierung der Lager \n", + "1 Unerwarteter Fehler Motorüberhitzung \n", + "2 Reguläre Wartung Überprüfung der Hydraulik \n", + "3 Unerwarteter Fehler Elektronikfehler \n", + "4 Reguläre Wartung Kalibrierung der Sensoren \n", + "\n", + " action \n", + "0 Nachfüllen des Schmiermittels \n", + "1 Austausch des Kühlgebläses \n", + "2 Reinigung und Nachfüllen der Hydraulikflüssigkeit \n", + "3 Austausch der defekten Platine \n", + "4 Justierung und Test der Sensoren " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data = read_dummy_data(pth_source)\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "aa6d3af2-31c7-44ee-a3a8-4201b559038f", + "metadata": {}, + "outputs": [], + "source": [ + "def make_subset(data, target_type, type_mapping=TYPE_MAPPING):\n", + " Entry = namedtuple('ProblemActionPairs', ['type', 'problem', 'action'])\n", + " entries = []\n", + " data_subset = data.loc[data['type']==target_type,:].copy()\n", + "\n", + " for row in data_subset.itertuples(index=False):\n", + " type_mapped = type_mapping[row.type]\n", + " entries.append(Entry(type_mapped, row.problem, row.action))\n", + "\n", + " return entries" + ] + }, + { + "cell_type": "markdown", + "id": "79bb0e96-3e04-458e-bbfb-dbd11a5386b9", + "metadata": {}, + "source": [ + "## Activity Types\n", + "\n", + "- relevant activity types:\n", + " - 'Reparaturauftrag (Portal)'\n", + " - 'Störungsmeldung'\n", + " - 'Wartung'\n", + "- ``regular`` --> 'Wartung'\n", + "- ``failures`` --> 'Störungsmeldung'" + ] + }, + { + "cell_type": "markdown", + "id": "2ec7a69d-80a6-4ede-928f-3ad933d3e090", + "metadata": {}, + "source": [ + "### Failures" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "668c0275-c8d8-4390-8857-a2ada566d786", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[ProblemActionPairs(type='Störungsmeldung', problem='Motorüberhitzung', action='Austausch des Kühlgebläses'),\n", + " ProblemActionPairs(type='Störungsmeldung', problem='Elektronikfehler', action='Austausch der defekten Platine'),\n", + " ProblemActionPairs(type='Störungsmeldung', problem='Getriebeausfall', action='Reparatur und Austausch der beschädigten Zahnräder'),\n", + " ProblemActionPairs(type='Störungsmeldung', problem='Leckage in der Hydraulikleitung', action='Abdichtung der Leckstelle und Nachfüllen der Hydraulikflüssigkeit'),\n", + " ProblemActionPairs(type='Störungsmeldung', problem='Ausfall der Steuerungseinheit', action='Neustart und Software-Update der Steuerungseinheit'),\n", + " ProblemActionPairs(type='Störungsmeldung', problem='Bruch eines Zahnriemens', action='Austausch des Zahnriemens'),\n", + " ProblemActionPairs(type='Störungsmeldung', problem='Kurzschluss im Schaltschrank', action='Austausch der Sicherungen und Kabel'),\n", + " ProblemActionPairs(type='Störungsmeldung', problem='Vibrationsprobleme am Motor', action='Auswuchten des Motors und Austausch der Dämpfer'),\n", + " ProblemActionPairs(type='Störungsmeldung', problem='Fehlfunktion der Hydraulikpumpe', action='Austausch der Hydraulikpumpe'),\n", + " ProblemActionPairs(type='Störungsmeldung', problem='Bruch eines Förderbands', action='Austausch des Förderbands')]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "failures = make_subset(data, target_type='Unerwarteter Fehler')\n", + "failures[:10]" + ] + }, + { + "cell_type": "markdown", + "id": "4f784dba-5e0a-41aa-9005-3e310fda47cb", + "metadata": {}, + "source": [ + "### Regular Maintenance" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d4b8ca4a-4230-463e-bb74-f965b0732155", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[ProblemActionPairs(type='Wartung', problem='Schmierung der Lager', action='Nachfüllen des Schmiermittels'),\n", + " ProblemActionPairs(type='Wartung', problem='Überprüfung der Hydraulik', action='Reinigung und Nachfüllen der Hydraulikflüssigkeit'),\n", + " ProblemActionPairs(type='Wartung', problem='Kalibrierung der Sensoren', action='Justierung und Test der Sensoren'),\n", + " ProblemActionPairs(type='Wartung', problem='Reinigung der Luftfilter', action='Austausch der Luftfilter'),\n", + " ProblemActionPairs(type='Wartung', problem='Überprüfung der Sicherheitsvorrichtungen', action='Funktionstest und Justierung der Sicherheitsvorrichtungen'),\n", + " ProblemActionPairs(type='Wartung', problem='Inspektion der Förderbänder', action='Einstellung und Austausch abgenutzter Teile'),\n", + " ProblemActionPairs(type='Wartung', problem='Überprüfung der Druckventile', action='Reinigung und Einstellung der Druckventile'),\n", + " ProblemActionPairs(type='Wartung', problem='Test der Not-Aus-Schalter', action='Test und Austausch defekter Not-Aus-Schalter'),\n", + " ProblemActionPairs(type='Wartung', problem='Überprüfung der Kühlmittelsysteme', action='Nachfüllen und Entlüftung des Kühlmittelsystems'),\n", + " ProblemActionPairs(type='Wartung', problem='Kontrolle der Lichtschranken', action='Reinigung und Neujustierung der Lichtschranken')]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "regular = make_subset(data, target_type='Reguläre Wartung')\n", + "regular[:10]" + ] + }, + { + "cell_type": "markdown", + "id": "aaf5b6bd-a7bf-4c6b-a969-566cd90d2353", + "metadata": {}, + "source": [ + "## ObjectIDs and HObjektTexts" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bf2d380d-4103-40b0-a99d-7770e73a9ef5", + "metadata": {}, + "outputs": [], + "source": [ + "def random_objects(mapping, rng, n_samples):\n", + " max_val = max(mapping.keys())\n", + " rands = rng.integers(1, max_val+1, size=n_samples)\n", + "\n", + " obj_ids = rands.tolist()\n", + " texts =[mapping[obj_id] for obj_id in obj_ids]\n", + "\n", + " return obj_ids, texts" + ] + }, + { + "cell_type": "markdown", + "id": "708521a4-a93c-4d29-9be7-19a29fd8aa7d", + "metadata": {}, + "source": [ + "## Random Dates" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "bff4fc9e-7a61-42df-abcb-1540e2d04b80", + "metadata": {}, + "outputs": [], + "source": [ + "def random_dates(start, end, rng, n_samples):\n", + "\n", + " start_u = start.value//10**9\n", + " end_u = end.value//10**9\n", + " days_to_finish = rng.exponential(1.3, n_samples).astype(np.int_)\n", + " td = pd.to_timedelta(days_to_finish, unit='day')\n", + "\n", + " creation_dates = pd.to_datetime(rng.integers(start_u, end_u, n_samples), unit='s').normalize()\n", + " done_dates = creation_dates + td\n", + "\n", + " return creation_dates.to_list(), done_dates.to_list()" + ] + }, + { + "cell_type": "markdown", + "id": "d2a77202-0eb9-4390-89af-e8e60e5a1e34", + "metadata": {}, + "source": [ + "## Random descriptions" + ] + }, + { + "cell_type": "markdown", + "id": "d360fe54-36f2-4f35-a42c-7ca09a7599c3", + "metadata": {}, + "source": [ + "proportions:\n", + "- regular: 0.6\n", + "- failure: 0.4" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "626a290b-a3ee-4c37-a754-b29ecca59f70", + "metadata": {}, + "outputs": [], + "source": [ + "def random_descriptions(failures, regular, target_prop_fail, rng, n_samples):\n", + " poss_per_entry_fail = target_prop_fail / len(failures)\n", + " poss_per_entry_regular = (1 - target_prop_fail) / len(regular)\n", + "\n", + " failure_possibilities = np.full(len(failures), poss_per_entry_fail)\n", + " regular_possibilities = np.full(len(regular), poss_per_entry_regular)\n", + " possibilities = np.concatenate((failure_possibilities, regular_possibilities))\n", + "\n", + " content_descriptions = failures.copy()\n", + " content_descriptions.extend(regular.copy())\n", + "\n", + " return rng.choice(content_descriptions, size=n_samples, p=possibilities)\n", + "\n", + "def description_parts(descriptions):\n", + " types = descriptions[:,0].tolist()\n", + " todo = descriptions[:,1].tolist()\n", + " dones = descriptions[:,2].tolist()\n", + "\n", + " return types, todo, dones" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8999cba-a460-4f67-901f-b7936f04cd74", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "39f72300-d73b-431f-89ee-af85e7bcdccc", + "metadata": {}, + "source": [ + "# Complete Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "8eb838de-d28e-4499-a63a-a708a58e0c6f", + "metadata": {}, + "outputs": [], + "source": [ + "def create_dataset(df_skelleton, type_failure, type_regular, starting_date, ending_date, rng, n_samples):\n", + " df_dict = df_skelleton.copy()\n", + " \n", + " failures = make_subset(data, target_type=type_failure)\n", + " regular = make_subset(data, target_type=type_regular)\n", + " \n", + " event_ids = list(range(1,n_samples+1))\n", + " obj_ids, txts = random_objects(OBJ_IDS_2_TXT, rng, n_samples)\n", + " creation_dates, done_dates = random_dates(starting_date, ending_date, rng, n_samples)\n", + " process_date = creation_dates.copy()\n", + " done_start_date = done_dates.copy()\n", + " descriptions = random_descriptions(failures, regular, TOTAL_POSSIBILITY_FAILURES, rng, n_samples)\n", + " types, todo, dones = description_parts(descriptions)\n", + "\n", + " df_dict.update(\n", + " VorgangsID=event_ids,\n", + " ObjektID=obj_ids,\n", + " HObjektText=txts,\n", + " VorgangsTypName=types,\n", + " VorgangsBeschreibung=todo,\n", + " ErledigungsBeschreibung=dones,\n", + " ErstellungsDatum=creation_dates,\n", + " VorgangsDatum=process_date,\n", + " Arbeitsbeginn=done_start_date,\n", + " ErledigungsDatum=done_dates,\n", + " )\n", + " df = pd.DataFrame.from_dict(df_dict)\n", + " df = df.sort_values(by='ErstellungsDatum', ascending=True)\n", + " df = df.reset_index(drop=True)\n", + " df['VorgangsID'] = event_ids\n", + "\n", + " return df.copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "195775b3-e44a-4d80-92bc-799093bd4ef2", + "metadata": {}, + "outputs": [], + "source": [ + "df = create_dataset(\n", + " df_skelleton=DF_SKELLETON,\n", + " type_failure='Unerwarteter Fehler',\n", + " type_regular='Reguläre Wartung',\n", + " starting_date=STARTING_DATE,\n", + " ending_date=ENDING_DATE,\n", + " rng=RNG,\n", + " n_samples=N_SAMPLES,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d3182c98-b57a-4619-aa41-8ab4a90bd1f2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
VorgangsIDObjektIDHObjektTextVorgangsTypNameVorgangsBeschreibungErledigungsBeschreibungErstellungsDatumVorgangsDatumArbeitsbeginnErledigungsDatum
012Schleifmaschine-S4x87StörungsmeldungÖlleckage durch undichten ÖlsumpfAbdichtung und Austausch des Ölsumpfs2022-01-012022-01-012022-01-012022-01-01
122Schleifmaschine-S4x87WartungÜberprüfung der SchwingungsdämpferAustausch und Justierung der Schwingungsdämpfer2022-01-032022-01-032022-01-032022-01-03
231Fräsmaschine-FS435XWartungÜberprüfung der KühlmittelsystemeNachfüllen und Entlüftung des Kühlmittelsystems2022-01-052022-01-052022-01-052022-01-05
343Bohrbearbeitungszentrum-BBZ35StörungsmeldungBlockierung der FörderschneckeBeseitigung der Blockierung und Überprüfung de...2022-01-062022-01-062022-01-072022-01-07
453Bohrbearbeitungszentrum-BBZ35StörungsmeldungÜberhitzung durch mangelnde KühlmittelzirkulationReinigung der Leitungen und Austausch des Kühl...2022-01-062022-01-062022-01-092022-01-09
.................................
9959961Fräsmaschine-FS435XWartungTest der Not-Aus-SchalterTest und Austausch defekter Not-Aus-Schalter2024-08-032024-08-032024-08-032024-08-03
9969972Schleifmaschine-S4x87StörungsmeldungFehlfunktion der HydraulikpumpeAustausch der Hydraulikpumpe2024-08-052024-08-052024-08-062024-08-06
9979983Bohrbearbeitungszentrum-BBZ35WartungKalibrierung der SensorenJustierung und Test der Sensoren2024-08-052024-08-052024-08-072024-08-07
9989992Schleifmaschine-S4x87WartungÜberprüfung der HydraulikzylinderNachjustierung und Schmierung der Hydraulikzyl...2024-08-052024-08-052024-08-052024-08-05
99910002Schleifmaschine-S4x87WartungInspektion der SchutzabdeckungenReparatur und Austausch beschädigter Abdeckungen2024-08-062024-08-062024-08-072024-08-07
\n", + "

1000 rows × 10 columns

\n", + "
" + ], + "text/plain": [ + " VorgangsID ObjektID HObjektText VorgangsTypName \\\n", + "0 1 2 Schleifmaschine-S4x87 Störungsmeldung \n", + "1 2 2 Schleifmaschine-S4x87 Wartung \n", + "2 3 1 Fräsmaschine-FS435X Wartung \n", + "3 4 3 Bohrbearbeitungszentrum-BBZ35 Störungsmeldung \n", + "4 5 3 Bohrbearbeitungszentrum-BBZ35 Störungsmeldung \n", + ".. ... ... ... ... \n", + "995 996 1 Fräsmaschine-FS435X Wartung \n", + "996 997 2 Schleifmaschine-S4x87 Störungsmeldung \n", + "997 998 3 Bohrbearbeitungszentrum-BBZ35 Wartung \n", + "998 999 2 Schleifmaschine-S4x87 Wartung \n", + "999 1000 2 Schleifmaschine-S4x87 Wartung \n", + "\n", + " VorgangsBeschreibung \\\n", + "0 Ölleckage durch undichten Ölsumpf \n", + "1 Überprüfung der Schwingungsdämpfer \n", + "2 Überprüfung der Kühlmittelsysteme \n", + "3 Blockierung der Förderschnecke \n", + "4 Überhitzung durch mangelnde Kühlmittelzirkulation \n", + ".. ... \n", + "995 Test der Not-Aus-Schalter \n", + "996 Fehlfunktion der Hydraulikpumpe \n", + "997 Kalibrierung der Sensoren \n", + "998 Überprüfung der Hydraulikzylinder \n", + "999 Inspektion der Schutzabdeckungen \n", + "\n", + " ErledigungsBeschreibung ErstellungsDatum \\\n", + "0 Abdichtung und Austausch des Ölsumpfs 2022-01-01 \n", + "1 Austausch und Justierung der Schwingungsdämpfer 2022-01-03 \n", + "2 Nachfüllen und Entlüftung des Kühlmittelsystems 2022-01-05 \n", + "3 Beseitigung der Blockierung und Überprüfung de... 2022-01-06 \n", + "4 Reinigung der Leitungen und Austausch des Kühl... 2022-01-06 \n", + ".. ... ... \n", + "995 Test und Austausch defekter Not-Aus-Schalter 2024-08-03 \n", + "996 Austausch der Hydraulikpumpe 2024-08-05 \n", + "997 Justierung und Test der Sensoren 2024-08-05 \n", + "998 Nachjustierung und Schmierung der Hydraulikzyl... 2024-08-05 \n", + "999 Reparatur und Austausch beschädigter Abdeckungen 2024-08-06 \n", + "\n", + " VorgangsDatum Arbeitsbeginn ErledigungsDatum \n", + "0 2022-01-01 2022-01-01 2022-01-01 \n", + "1 2022-01-03 2022-01-03 2022-01-03 \n", + "2 2022-01-05 2022-01-05 2022-01-05 \n", + "3 2022-01-06 2022-01-07 2022-01-07 \n", + "4 2022-01-06 2022-01-09 2022-01-09 \n", + ".. ... ... ... \n", + "995 2024-08-03 2024-08-03 2024-08-03 \n", + "996 2024-08-05 2024-08-06 2024-08-06 \n", + "997 2024-08-05 2024-08-07 2024-08-07 \n", + "998 2024-08-05 2024-08-05 2024-08-05 \n", + "999 2024-08-06 2024-08-07 2024-08-07 \n", + "\n", + "[1000 rows x 10 columns]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "2bb03fdb-ea45-46a1-81b4-525f7568355c", + "metadata": {}, + "outputs": [], + "source": [ + "# df.to_excel(pth_dest)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "ff9f6f80-b709-4011-89fe-90c8812d7e7b", + "metadata": {}, + "outputs": [], + "source": [ + "df.to_csv(pth_dest, sep=';', encoding='cp1252', index=False, date_format='%d.%m.%Y')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2871889-f128-419c-8e89-d8eb48ceb2e1", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "cdba82ad-d4d1-4266-ad41-8d90bb059956", + "metadata": {}, + "source": [ + "# Check processed data" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "af26cd9b-e5d1-46e1-b269-ac46de10dfe2", + "metadata": {}, + "outputs": [], + "source": [ + "pth_to_data = '../scripts/results/dummy_N_1000/'\n", + "pth_to_data = Path(pth_to_data)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "c7338787-716c-43c0-9d11-03567459f594", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[WindowsPath('../scripts/results/dummy_N_1000/Pipe-TargetFeature_Step-3_remove_NA.pkl'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/TIMELINE.pkl'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/Pipe-TargetFeature_Step-5_analyse_feature.pkl'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/TOKEN_ANALYSIS.pkl'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/TK-GRAPH_POSTPROCESSING.pkl'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/TokenGraph.graphml'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/TK-GRAPH_ANALYSIS.pkl'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/TokenGraph-filtered.graphml'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/TK-GRAPH_ANALYSIS_RESCALED.pkl'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/TokenGraph-directed-rescaled.graphml'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/TokenGraph-undirected-rescaled.graphml'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/token_graph.svg'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/token_graph_sub_1.svg'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/token_graph_sub_2.svg'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/token_graph_sub_3.svg'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/token_graph_sub_4.svg'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/token_graph_sub_5.svg'),\n", + " WindowsPath('../scripts/results/dummy_N_1000/Pipe-Graph_Static-Rendering_Step-6_build_subnetworks.pkl')]" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "files = list(pth_to_data.glob(r'*'))\n", + "files" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "1dd0da25-9097-46a1-bac8-dce281e17c5b", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "A:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\transformers\\utils\\generic.py:441: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n", + " _torch_pytree._register_pytree_node(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2024-08-07 13:37:19 +0000 | lang_main:io:INFO | Loaded TOML config file successfully.\n" + ] + } + ], + "source": [ + "from lang_main import io" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "33ae3e52-f638-40a0-b243-6578cde52a19", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WindowsPath('../scripts/results/dummy_N_1000/TIMELINE.pkl')" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "files[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "fc598842-f218-4895-8d1e-20b09f9e6d12", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2024-08-07 13:51:31 +0000 | lang_main:io:INFO | Loaded file successfully.\n" + ] + } + ], + "source": [ + "(data,) = io.load_pickle(files[1])" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "3cbffa6c-4199-4a9f-b041-3c34fdbc7266", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
VorgangsIDObjektIDHObjektTextVorgangsTypNameVorgangsBeschreibungErledigungsBeschreibungErstellungsDatumVorgangsDatumArbeitsbeginnErledigungsDatum
012Schleifmaschine-S4x87StörungsmeldungÖlleckage durch undichten ÖlsumpfAbdichtung und Austausch des Ölsumpfs2022-01-012022-01-012022-01-012022-01-01
122Schleifmaschine-S4x87WartungÜberprüfung der SchwingungsdämpferAustausch und Justierung der Schwingungsdämpfer2022-01-032022-01-032022-01-032022-01-03
231Fräsmaschine-FS435XWartungÜberprüfung der KühlmittelsystemeNachfüllen und Entlüftung des Kühlmittelsystems2022-01-052022-01-052022-01-052022-01-05
343Bohrbearbeitungszentrum-BBZ35StörungsmeldungBlockierung der FörderschneckeBeseitigung der Blockierung und Überprüfung de...2022-01-062022-01-062022-01-072022-01-07
453Bohrbearbeitungszentrum-BBZ35StörungsmeldungÜberhitzung durch mangelnde KühlmittelzirkulationReinigung der Leitungen und Austausch des Kühl...2022-01-062022-01-062022-01-092022-01-09
.................................
9959961Fräsmaschine-FS435XWartungTest der Not-Aus-SchalterTest und Austausch defekter Not-Aus-Schalter2024-08-032024-08-032024-08-032024-08-03
9969972Schleifmaschine-S4x87StörungsmeldungFehlfunktion der HydraulikpumpeAustausch der Hydraulikpumpe2024-08-052024-08-052024-08-062024-08-06
9979983Bohrbearbeitungszentrum-BBZ35WartungKalibrierung der SensorenJustierung und Test der Sensoren2024-08-052024-08-052024-08-072024-08-07
9989992Schleifmaschine-S4x87WartungÜberprüfung der HydraulikzylinderNachjustierung und Schmierung der Hydraulikzyl...2024-08-052024-08-052024-08-052024-08-05
99910002Schleifmaschine-S4x87WartungInspektion der SchutzabdeckungenReparatur und Austausch beschädigter Abdeckungen2024-08-062024-08-062024-08-072024-08-07
\n", + "

1000 rows × 10 columns

\n", + "
" + ], + "text/plain": [ + " VorgangsID ObjektID HObjektText VorgangsTypName \\\n", + "0 1 2 Schleifmaschine-S4x87 Störungsmeldung \n", + "1 2 2 Schleifmaschine-S4x87 Wartung \n", + "2 3 1 Fräsmaschine-FS435X Wartung \n", + "3 4 3 Bohrbearbeitungszentrum-BBZ35 Störungsmeldung \n", + "4 5 3 Bohrbearbeitungszentrum-BBZ35 Störungsmeldung \n", + ".. ... ... ... ... \n", + "995 996 1 Fräsmaschine-FS435X Wartung \n", + "996 997 2 Schleifmaschine-S4x87 Störungsmeldung \n", + "997 998 3 Bohrbearbeitungszentrum-BBZ35 Wartung \n", + "998 999 2 Schleifmaschine-S4x87 Wartung \n", + "999 1000 2 Schleifmaschine-S4x87 Wartung \n", + "\n", + " VorgangsBeschreibung \\\n", + "0 Ölleckage durch undichten Ölsumpf \n", + "1 Überprüfung der Schwingungsdämpfer \n", + "2 Überprüfung der Kühlmittelsysteme \n", + "3 Blockierung der Förderschnecke \n", + "4 Überhitzung durch mangelnde Kühlmittelzirkulation \n", + ".. ... \n", + "995 Test der Not-Aus-Schalter \n", + "996 Fehlfunktion der Hydraulikpumpe \n", + "997 Kalibrierung der Sensoren \n", + "998 Überprüfung der Hydraulikzylinder \n", + "999 Inspektion der Schutzabdeckungen \n", + "\n", + " ErledigungsBeschreibung ErstellungsDatum \\\n", + "0 Abdichtung und Austausch des Ölsumpfs 2022-01-01 \n", + "1 Austausch und Justierung der Schwingungsdämpfer 2022-01-03 \n", + "2 Nachfüllen und Entlüftung des Kühlmittelsystems 2022-01-05 \n", + "3 Beseitigung der Blockierung und Überprüfung de... 2022-01-06 \n", + "4 Reinigung der Leitungen und Austausch des Kühl... 2022-01-06 \n", + ".. ... ... \n", + "995 Test und Austausch defekter Not-Aus-Schalter 2024-08-03 \n", + "996 Austausch der Hydraulikpumpe 2024-08-05 \n", + "997 Justierung und Test der Sensoren 2024-08-05 \n", + "998 Nachjustierung und Schmierung der Hydraulikzyl... 2024-08-05 \n", + "999 Reparatur und Austausch beschädigter Abdeckungen 2024-08-06 \n", + "\n", + " VorgangsDatum Arbeitsbeginn ErledigungsDatum \n", + "0 2022-01-01 2022-01-01 2022-01-01 \n", + "1 2022-01-03 2022-01-03 2022-01-03 \n", + "2 2022-01-05 2022-01-05 2022-01-05 \n", + "3 2022-01-06 2022-01-07 2022-01-07 \n", + "4 2022-01-06 2022-01-09 2022-01-09 \n", + ".. ... ... ... \n", + "995 2024-08-03 2024-08-03 2024-08-03 \n", + "996 2024-08-05 2024-08-06 2024-08-06 \n", + "997 2024-08-05 2024-08-07 2024-08-07 \n", + "998 2024-08-05 2024-08-05 2024-08-05 \n", + "999 2024-08-06 2024-08-07 2024-08-07 \n", + "\n", + "[1000 rows x 10 columns]" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "fd422d51-6118-47aa-80a1-6e80819a3205", + "metadata": {}, + "outputs": [], + "source": [ + "t = data.copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "4225af01-b9df-4b27-aae2-b06257b0dd3a", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "Can only use .dt accessor with datetimelike values", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[37], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mt\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mErledigungsDatum\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdt\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\generic.py:6299\u001b[0m, in \u001b[0;36mNDFrame.__getattr__\u001b[1;34m(self, name)\u001b[0m\n\u001b[0;32m 6292\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[0;32m 6293\u001b[0m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_internal_names_set\n\u001b[0;32m 6294\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_metadata\n\u001b[0;32m 6295\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_accessors\n\u001b[0;32m 6296\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_info_axis\u001b[38;5;241m.\u001b[39m_can_hold_identifiers_and_holds_name(name)\n\u001b[0;32m 6297\u001b[0m ):\n\u001b[0;32m 6298\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m[name]\n\u001b[1;32m-> 6299\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mobject\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__getattribute__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\accessor.py:224\u001b[0m, in \u001b[0;36mCachedAccessor.__get__\u001b[1;34m(self, obj, cls)\u001b[0m\n\u001b[0;32m 221\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 222\u001b[0m \u001b[38;5;66;03m# we're accessing the attribute of the class, i.e., Dataset.geo\u001b[39;00m\n\u001b[0;32m 223\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_accessor\n\u001b[1;32m--> 224\u001b[0m accessor_obj \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_accessor\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 225\u001b[0m \u001b[38;5;66;03m# Replace the property with the accessor object. Inspired by:\u001b[39;00m\n\u001b[0;32m 226\u001b[0m \u001b[38;5;66;03m# https://www.pydanny.com/cached-property.html\u001b[39;00m\n\u001b[0;32m 227\u001b[0m \u001b[38;5;66;03m# We need to use object.__setattr__ because we overwrite __setattr__ on\u001b[39;00m\n\u001b[0;32m 228\u001b[0m \u001b[38;5;66;03m# NDFrame\u001b[39;00m\n\u001b[0;32m 229\u001b[0m \u001b[38;5;28mobject\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;21m__setattr__\u001b[39m(obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_name, accessor_obj)\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\indexes\\accessors.py:643\u001b[0m, in \u001b[0;36mCombinedDatetimelikeProperties.__new__\u001b[1;34m(cls, data)\u001b[0m\n\u001b[0;32m 640\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(data\u001b[38;5;241m.\u001b[39mdtype, PeriodDtype):\n\u001b[0;32m 641\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m PeriodProperties(data, orig)\n\u001b[1;32m--> 643\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mAttributeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCan only use .dt accessor with datetimelike values\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "\u001b[1;31mAttributeError\u001b[0m: Can only use .dt accessor with datetimelike values" + ] + } + ], + "source": [ + "t['ErledigungsDatum'].dt" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "9ad24677-b0be-4f4e-9067-b4746e0ba039", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "Can only use .dt accessor with datetimelike values", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[38], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mt\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mErstellungsDatum\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdt\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\generic.py:6299\u001b[0m, in \u001b[0;36mNDFrame.__getattr__\u001b[1;34m(self, name)\u001b[0m\n\u001b[0;32m 6292\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[0;32m 6293\u001b[0m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_internal_names_set\n\u001b[0;32m 6294\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_metadata\n\u001b[0;32m 6295\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_accessors\n\u001b[0;32m 6296\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_info_axis\u001b[38;5;241m.\u001b[39m_can_hold_identifiers_and_holds_name(name)\n\u001b[0;32m 6297\u001b[0m ):\n\u001b[0;32m 6298\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m[name]\n\u001b[1;32m-> 6299\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mobject\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__getattribute__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\accessor.py:224\u001b[0m, in \u001b[0;36mCachedAccessor.__get__\u001b[1;34m(self, obj, cls)\u001b[0m\n\u001b[0;32m 221\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 222\u001b[0m \u001b[38;5;66;03m# we're accessing the attribute of the class, i.e., Dataset.geo\u001b[39;00m\n\u001b[0;32m 223\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_accessor\n\u001b[1;32m--> 224\u001b[0m accessor_obj \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_accessor\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 225\u001b[0m \u001b[38;5;66;03m# Replace the property with the accessor object. Inspired by:\u001b[39;00m\n\u001b[0;32m 226\u001b[0m \u001b[38;5;66;03m# https://www.pydanny.com/cached-property.html\u001b[39;00m\n\u001b[0;32m 227\u001b[0m \u001b[38;5;66;03m# We need to use object.__setattr__ because we overwrite __setattr__ on\u001b[39;00m\n\u001b[0;32m 228\u001b[0m \u001b[38;5;66;03m# NDFrame\u001b[39;00m\n\u001b[0;32m 229\u001b[0m \u001b[38;5;28mobject\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;21m__setattr__\u001b[39m(obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_name, accessor_obj)\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\indexes\\accessors.py:643\u001b[0m, in \u001b[0;36mCombinedDatetimelikeProperties.__new__\u001b[1;34m(cls, data)\u001b[0m\n\u001b[0;32m 640\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(data\u001b[38;5;241m.\u001b[39mdtype, PeriodDtype):\n\u001b[0;32m 641\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m PeriodProperties(data, orig)\n\u001b[1;32m--> 643\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mAttributeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCan only use .dt accessor with datetimelike values\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "\u001b[1;31mAttributeError\u001b[0m: Can only use .dt accessor with datetimelike values" + ] + } + ], + "source": [ + "t['ErstellungsDatum'].dt" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "de697da1-2a4d-465f-988e-5d0a68840167", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unsupported operand type(s) for -: 'str' and 'str'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\ops\\array_ops.py:218\u001b[0m, in \u001b[0;36m_na_arithmetic_op\u001b[1;34m(left, right, op, is_cmp)\u001b[0m\n\u001b[0;32m 217\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 218\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43mleft\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mright\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 219\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m:\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\computation\\expressions.py:242\u001b[0m, in \u001b[0;36mevaluate\u001b[1;34m(op, a, b, use_numexpr)\u001b[0m\n\u001b[0;32m 240\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m use_numexpr:\n\u001b[0;32m 241\u001b[0m \u001b[38;5;66;03m# error: \"None\" not callable\u001b[39;00m\n\u001b[1;32m--> 242\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_evaluate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mop\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop_str\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mb\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# type: ignore[misc]\u001b[39;00m\n\u001b[0;32m 243\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _evaluate_standard(op, op_str, a, b)\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\computation\\expressions.py:73\u001b[0m, in \u001b[0;36m_evaluate_standard\u001b[1;34m(op, op_str, a, b)\u001b[0m\n\u001b[0;32m 72\u001b[0m _store_test_result(\u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[1;32m---> 73\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mop\u001b[49m\u001b[43m(\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mb\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[1;31mTypeError\u001b[0m: unsupported operand type(s) for -: 'str' and 'str'", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[36], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m t[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtest\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[43mt\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mErledigungsDatum\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mErstellungsDatum\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\ops\\common.py:76\u001b[0m, in \u001b[0;36m_unpack_zerodim_and_defer..new_method\u001b[1;34m(self, other)\u001b[0m\n\u001b[0;32m 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mNotImplemented\u001b[39m\n\u001b[0;32m 74\u001b[0m other \u001b[38;5;241m=\u001b[39m item_from_zerodim(other)\n\u001b[1;32m---> 76\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmethod\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mother\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\arraylike.py:194\u001b[0m, in \u001b[0;36mOpsMixin.__sub__\u001b[1;34m(self, other)\u001b[0m\n\u001b[0;32m 192\u001b[0m \u001b[38;5;129m@unpack_zerodim_and_defer\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m__sub__\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 193\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__sub__\u001b[39m(\u001b[38;5;28mself\u001b[39m, other):\n\u001b[1;32m--> 194\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_arith_method\u001b[49m\u001b[43m(\u001b[49m\u001b[43mother\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moperator\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msub\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\series.py:6135\u001b[0m, in \u001b[0;36mSeries._arith_method\u001b[1;34m(self, other, op)\u001b[0m\n\u001b[0;32m 6133\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_arith_method\u001b[39m(\u001b[38;5;28mself\u001b[39m, other, op):\n\u001b[0;32m 6134\u001b[0m \u001b[38;5;28mself\u001b[39m, other \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_align_for_op(other)\n\u001b[1;32m-> 6135\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mbase\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mIndexOpsMixin\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_arith_method\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mother\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\base.py:1382\u001b[0m, in \u001b[0;36mIndexOpsMixin._arith_method\u001b[1;34m(self, other, op)\u001b[0m\n\u001b[0;32m 1379\u001b[0m rvalues \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39marange(rvalues\u001b[38;5;241m.\u001b[39mstart, rvalues\u001b[38;5;241m.\u001b[39mstop, rvalues\u001b[38;5;241m.\u001b[39mstep)\n\u001b[0;32m 1381\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m np\u001b[38;5;241m.\u001b[39merrstate(\u001b[38;5;28mall\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mignore\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[1;32m-> 1382\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mops\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marithmetic_op\u001b[49m\u001b[43m(\u001b[49m\u001b[43mlvalues\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrvalues\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1384\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_construct_result(result, name\u001b[38;5;241m=\u001b[39mres_name)\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\ops\\array_ops.py:283\u001b[0m, in \u001b[0;36marithmetic_op\u001b[1;34m(left, right, op)\u001b[0m\n\u001b[0;32m 279\u001b[0m _bool_arith_check(op, left, right) \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n\u001b[0;32m 281\u001b[0m \u001b[38;5;66;03m# error: Argument 1 to \"_na_arithmetic_op\" has incompatible type\u001b[39;00m\n\u001b[0;32m 282\u001b[0m \u001b[38;5;66;03m# \"Union[ExtensionArray, ndarray[Any, Any]]\"; expected \"ndarray[Any, Any]\"\u001b[39;00m\n\u001b[1;32m--> 283\u001b[0m res_values \u001b[38;5;241m=\u001b[39m \u001b[43m_na_arithmetic_op\u001b[49m\u001b[43m(\u001b[49m\u001b[43mleft\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mright\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n\u001b[0;32m 285\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res_values\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\ops\\array_ops.py:227\u001b[0m, in \u001b[0;36m_na_arithmetic_op\u001b[1;34m(left, right, op, is_cmp)\u001b[0m\n\u001b[0;32m 219\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m:\n\u001b[0;32m 220\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m is_cmp \u001b[38;5;129;01mand\u001b[39;00m (\n\u001b[0;32m 221\u001b[0m left\u001b[38;5;241m.\u001b[39mdtype \u001b[38;5;241m==\u001b[39m \u001b[38;5;28mobject\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(right, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdtype\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m) \u001b[38;5;241m==\u001b[39m \u001b[38;5;28mobject\u001b[39m\n\u001b[0;32m 222\u001b[0m ):\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 225\u001b[0m \u001b[38;5;66;03m# Don't do this for comparisons, as that will handle complex numbers\u001b[39;00m\n\u001b[0;32m 226\u001b[0m \u001b[38;5;66;03m# incorrectly, see GH#32047\u001b[39;00m\n\u001b[1;32m--> 227\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43m_masked_arith_op\u001b[49m\u001b[43m(\u001b[49m\u001b[43mleft\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mright\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 228\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 229\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\pandas\\core\\ops\\array_ops.py:163\u001b[0m, in \u001b[0;36m_masked_arith_op\u001b[1;34m(x, y, op)\u001b[0m\n\u001b[0;32m 161\u001b[0m \u001b[38;5;66;03m# See GH#5284, GH#5035, GH#19448 for historical reference\u001b[39;00m\n\u001b[0;32m 162\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m mask\u001b[38;5;241m.\u001b[39many():\n\u001b[1;32m--> 163\u001b[0m result[mask] \u001b[38;5;241m=\u001b[39m \u001b[43mop\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxrav\u001b[49m\u001b[43m[\u001b[49m\u001b[43mmask\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43myrav\u001b[49m\u001b[43m[\u001b[49m\u001b[43mmask\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 165\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 166\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m is_scalar(y):\n", + "\u001b[1;31mTypeError\u001b[0m: unsupported operand type(s) for -: 'str' and 'str'" + ] + } + ], + "source": [ + "t['test'] = t['ErledigungsDatum'] - t['ErstellungsDatum']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2b1724e-f48d-41a3-98c6-710bef840ba5", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test-notebooks/lang_main.xml b/notebooks/lang_main.xml similarity index 100% rename from test-notebooks/lang_main.xml rename to notebooks/lang_main.xml diff --git a/test-notebooks/lang_main_config.toml b/notebooks/lang_main_config.toml similarity index 100% rename from test-notebooks/lang_main_config.toml rename to notebooks/lang_main_config.toml diff --git a/test-notebooks/misc.ipynb b/notebooks/misc.ipynb similarity index 97% rename from test-notebooks/misc.ipynb rename to notebooks/misc.ipynb index 7d0193d..ae63ff3 100644 --- a/test-notebooks/misc.ipynb +++ b/notebooks/misc.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "d46b6ce8-b51b-49e0-b494-fc24fda0f73f", "metadata": {}, "outputs": [], @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "6fde72d3-b95b-4d37-be71-a7d3661dd3f5", "metadata": {}, "outputs": [ @@ -40,7 +40,7 @@ "'You are connected to Cytoscape!'" ] }, - "execution_count": 3, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -52,46 +52,41 @@ { "cell_type": "code", "execution_count": null, - "id": "43eeb870-6f97-4029-ac0d-210315ccaabf", + "id": "187ced81-6304-49bd-afc7-c18e656bc9a3", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "af118d77-d87a-4687-be5b-e810a24c403e", "metadata": { "scrolled": true }, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2024-07-31 06:33:53 +0000 | io:INFO | Loaded TOML config file successfully.\n" - ] - }, { "name": "stderr", "output_type": "stream", "text": [ "A:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\transformers\\utils\\generic.py:441: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n", - " _torch_pytree._register_pytree_node(\n", - "A:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\huggingface_hub\\file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n", - " warnings.warn(\n", - "A:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\transformers\\utils\\generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n", - " _torch_pytree._register_pytree_node(\n", - "A:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\transformers\\utils\\generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n", - " _torch_pytree._register_pytree_node(\n", - "A:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\huggingface_hub\\file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n", - " warnings.warn(\n" + " _torch_pytree._register_pytree_node(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2024-08-07 07:36:54 +0000 | io:INFO | Loaded TOML config file successfully.\n" ] } ], "source": [ "from lang_main import io\n", "from lang_main.analysis.graphs import rescale_edge_weights, get_graph_metadata\n", + "from lang_main import model_loader\n", + "from lang_main.types import LanguageModels\n", + "from lang_main.constants import MODEL_LOADER_MAP\n", "\n", "from pathlib import Path\n", "import pickle\n", @@ -112,6 +107,88 @@ "#p4c.py4cytoscape_logger.detail_logger.addHandler(NullHandler())" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "59cbcf38-6fe1-403b-9c10-f107e28185f0", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a33cb410-f774-4cc9-b972-bf05df36d3d7", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "A:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\transformers\\utils\\generic.py:441: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n", + " _torch_pytree._register_pytree_node(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "spacy imports\n", + "end\n", + "Nothing\n", + "2024-08-07 07:51:22 +0000 | io:INFO | Loaded TOML config file successfully.\n" + ] + } + ], + "source": [ + "from lang_main import __init__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f55780a-a91e-49ef-a24f-503eaf2efae8", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2d91512f-8976-452e-acc9-4bff3dc33dd1", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "A:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\transformers\\utils\\generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n", + " _torch_pytree._register_pytree_node(\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model_loader.instantiate_model(MODEL_LOADER_MAP, LanguageModels.SPACY)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7fe9337f-a11b-4eab-ae46-a8a4ccf3f461", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": 3, @@ -3753,7 +3830,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 3, "id": "fcd9247f-c4f9-4f73-9fd3-2ab56700073f", "metadata": {}, "outputs": [ @@ -3761,23 +3838,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | Calling cytoscape_ping()\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀCalling cytoscape_version_info(base_url='http://127.0.0.1:1234/v1')\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀǀCalling cyrest_get('version', base_url='http://127.0.0.1:1234/v1')\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀǀHTTP GET(http://127.0.0.1:1234/v1/version)\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀǀOK[200], content: {\"apiVersion\":\"v1\",\"cytoscapeVersion\":\"3.10.2\"}\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀǀReturning 'cyrest_get': {'apiVersion': 'v1', 'cytoscapeVersion': '3.10.2'}\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀReturning 'cytoscape_version_info': {'apiVersion': 'v1', 'cytoscapeVersion': '3.10.2', 'automationAPIVersion': '1.9.0', 'py4cytoscapeVersion': '1.9.0'}\n", - "You are connected to Cytoscape!\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | Returning 'cytoscape_ping': 'You are connected to Cytoscape!'\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | --------------------\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | Calling cytoscape_version_info()\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀCalling cyrest_get('version', base_url='http://127.0.0.1:1234/v1')\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀHTTP GET(http://127.0.0.1:1234/v1/version)\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀOK[200], content: {\"apiVersion\":\"v1\",\"cytoscapeVersion\":\"3.10.2\"}\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | ǀReturning 'cyrest_get': {'apiVersion': 'v1', 'cytoscapeVersion': '3.10.2'}\n", - "2024-07-10 11:19:15 +0000 | py4cytoscape_logger:DEBUG | Returning 'cytoscape_version_info': {'apiVersion': 'v1', 'cytoscapeVersion': '3.10.2', 'automationAPIVersion': '1.9.0', 'py4cytoscapeVersion': '1.9.0'}\n", - "2024-07-10 11:19:16 +0000 | py4cytoscape_logger:DEBUG | --------------------\n" + "You are connected to Cytoscape!\n" ] }, { @@ -3789,7 +3850,7 @@ " 'py4cytoscapeVersion': '1.9.0'}" ] }, - "execution_count": 11, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -3803,7 +3864,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "b9290659-e33c-47fc-8d89-7aa3dd6e843a", "metadata": {}, "outputs": [], @@ -3815,7 +3876,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "979d6def-83ac-47f6-ac6f-0d20ddf48d48", "metadata": {}, "outputs": [ @@ -3882,7 +3943,7 @@ "3 node 3 B 5" ] }, - "execution_count": 6, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -3893,7 +3954,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "81702429-5735-48de-96a4-1f32c7c7d68c", "metadata": {}, "outputs": [ @@ -3965,7 +4026,7 @@ "3 node 2 node 3 interacts 9.9" ] }, - "execution_count": 7, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -3976,7 +4037,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "6b29d561-fffd-4a5b-91c1-8fb6a075ae4f", "metadata": {}, "outputs": [ @@ -3994,7 +4055,7 @@ "128" ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -4003,6 +4064,187 @@ "p4c.create_network_from_data_frames(nodes, edges, title=\"my first network\", collection=\"DataFrame Example\")" ] }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1e87b4d9-6ef0-4108-81ff-e0124e45b793", + "metadata": {}, + "outputs": [], + "source": [ + "p4c.hide_all_panels()" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "23706ea9-b661-428e-a4de-ac4543aafc76", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "''" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p4c.set_network_zoom_bypass(1.5, bypass=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "9c7e7fa0-8b17-43f6-9076-3e117748b06b", + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "In cyrest_delete(): Bypass Visual Property does not exist: NETWORK_SCALE_FACTOR\n" + ] + }, + { + "ename": "CyError", + "evalue": "In cyrest_delete(): Bypass Visual Property does not exist: NETWORK_SCALE_FACTOR", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mHTTPError\u001b[0m Traceback (most recent call last)", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\commands.py:109\u001b[0m, in \u001b[0;36mcyrest_delete\u001b[1;34m(operation, parameters, base_url, require_json)\u001b[0m\n\u001b[0;32m 108\u001b[0m r \u001b[38;5;241m=\u001b[39m _do_request(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mDELETE\u001b[39m\u001b[38;5;124m'\u001b[39m, url, params\u001b[38;5;241m=\u001b[39mparameters, base_url\u001b[38;5;241m=\u001b[39mbase_url)\n\u001b[1;32m--> 109\u001b[0m \u001b[43mr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mraise_for_status\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 110\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\requests\\models.py:1021\u001b[0m, in \u001b[0;36mResponse.raise_for_status\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 1020\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m http_error_msg:\n\u001b[1;32m-> 1021\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m HTTPError(http_error_msg, response\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m)\n", + "\u001b[1;31mHTTPError\u001b[0m: 404 Client Error: Not Found for url: http://127.0.0.1:1234/v1/networks/3004/views/3129/network/NETWORK_SCALE_FACTOR/bypass", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[1;31mCyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[25], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mp4c\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mclear_network_zoom_bypass\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\py4cytoscape_logger.py:133\u001b[0m, in \u001b[0;36mcy_log..wrapper_log\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m log_return(func, value)\n\u001b[0;32m 132\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m--> 133\u001b[0m \u001b[43mlog_exception\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43me\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 134\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 135\u001b[0m log_finally()\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\py4cytoscape_logger.py:130\u001b[0m, in \u001b[0;36mcy_log..wrapper_log\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 128\u001b[0m log_incoming(func, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 129\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 130\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# Call function being logged\u001b[39;00m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m log_return(func, value)\n\u001b[0;32m 132\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\style_bypasses.py:2726\u001b[0m, in \u001b[0;36mclear_network_zoom_bypass\u001b[1;34m(network, base_url)\u001b[0m\n\u001b[0;32m 2697\u001b[0m \u001b[38;5;129m@cy_log\u001b[39m\n\u001b[0;32m 2698\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mclear_network_zoom_bypass\u001b[39m(network\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, base_url\u001b[38;5;241m=\u001b[39mDEFAULT_BASE_URL):\n\u001b[0;32m 2699\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Clear the bypass value for the scale factor for the network, effectively restoring prior default values.\u001b[39;00m\n\u001b[0;32m 2700\u001b[0m \n\u001b[0;32m 2701\u001b[0m \u001b[38;5;124;03m Args:\u001b[39;00m\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 2724\u001b[0m \u001b[38;5;124;03m clearing this property will throw an exception.\u001b[39;00m\n\u001b[0;32m 2725\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m-> 2726\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[43mclear_network_property_bypass\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mNETWORK_SCALE_FACTOR\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnetwork\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnetwork\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbase_url\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbase_url\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 2727\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\py4cytoscape_logger.py:133\u001b[0m, in \u001b[0;36mcy_log..wrapper_log\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m log_return(func, value)\n\u001b[0;32m 132\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m--> 133\u001b[0m \u001b[43mlog_exception\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43me\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 134\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 135\u001b[0m log_finally()\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\py4cytoscape_logger.py:130\u001b[0m, in \u001b[0;36mcy_log..wrapper_log\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 128\u001b[0m log_incoming(func, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 129\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 130\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# Call function being logged\u001b[39;00m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m log_return(func, value)\n\u001b[0;32m 132\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\style_bypasses.py:516\u001b[0m, in \u001b[0;36mclear_network_property_bypass\u001b[1;34m(visual_property, network, base_url)\u001b[0m\n\u001b[0;32m 512\u001b[0m view_suid \u001b[38;5;241m=\u001b[39m network_views\u001b[38;5;241m.\u001b[39mget_network_views(net_suid, base_url\u001b[38;5;241m=\u001b[39mbase_url)[\u001b[38;5;241m0\u001b[39m]\n\u001b[0;32m 514\u001b[0m visual_property \u001b[38;5;241m=\u001b[39m normalize_prop_name(visual_property)\n\u001b[1;32m--> 516\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[43mcommands\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcyrest_delete\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mnetworks/\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mnet_suid\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m/views/\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mview_suid\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m/network/\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mvisual_property\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m/bypass\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 517\u001b[0m \u001b[43m \u001b[49m\u001b[43mbase_url\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbase_url\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 518\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\py4cytoscape_logger.py:133\u001b[0m, in \u001b[0;36mcy_log..wrapper_log\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m log_return(func, value)\n\u001b[0;32m 132\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m--> 133\u001b[0m \u001b[43mlog_exception\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43me\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 134\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 135\u001b[0m log_finally()\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\py4cytoscape_logger.py:130\u001b[0m, in \u001b[0;36mcy_log..wrapper_log\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 128\u001b[0m log_incoming(func, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 129\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 130\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# Call function being logged\u001b[39;00m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m log_return(func, value)\n\u001b[0;32m 132\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\commands.py:118\u001b[0m, in \u001b[0;36mcyrest_delete\u001b[1;34m(operation, parameters, base_url, require_json)\u001b[0m\n\u001b[0;32m 116\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m r\u001b[38;5;241m.\u001b[39mtext\n\u001b[0;32m 117\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m requests\u001b[38;5;241m.\u001b[39mexceptions\u001b[38;5;241m.\u001b[39mRequestException \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m--> 118\u001b[0m \u001b[43m_handle_error\u001b[49m\u001b[43m(\u001b[49m\u001b[43me\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mA:\\Arbeitsaufgaben\\lang-main\\.venv\\Lib\\site-packages\\py4cytoscape\\commands.py:683\u001b[0m, in \u001b[0;36m_handle_error\u001b[1;34m(e, force_cy_error)\u001b[0m\n\u001b[0;32m 681\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 682\u001b[0m show_error(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mIn \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mcaller\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00me\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mcontent\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m--> 683\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m e\n", + "\u001b[1;31mCyError\u001b[0m: In cyrest_delete(): Bypass Visual Property does not exist: NETWORK_SCALE_FACTOR" + ] + } + ], + "source": [ + "p4c.clear_network_zoom_bypass()" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "234855d1-a961-4dd7-9b2e-a96d7acc1142", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{}" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p4c.fit_content()" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "79ccddf6-3b0f-4151-a333-05b6e7b5ed8a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.631300425888794" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zoom = p4c.get_network_zoom()\n", + "zoom" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "dc8f2bcc-9836-4476-bfa4-1d83308375ac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.5660484088532423" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zoom * 0.96" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "3e3106c6-d9f4-41cd-8a5a-452cd32b25c0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "''" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p4c.set_network_zoom_bypass(zoom * 0.96, bypass=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05a19acc-8af7-45d0-8902-1e9776824a38", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4647c594-46f1-4e12-9927-e73ca5a0486a", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": 10, diff --git a/test-notebooks/styles_template.xml b/notebooks/styles_template.xml similarity index 100% rename from test-notebooks/styles_template.xml rename to notebooks/styles_template.xml diff --git a/test-notebooks/timeline_analysis.ipynb b/notebooks/timeline_analysis.ipynb similarity index 100% rename from test-notebooks/timeline_analysis.ipynb rename to notebooks/timeline_analysis.ipynb diff --git a/test-notebooks/truncate_dataset.ipynb b/notebooks/truncate_dataset.ipynb similarity index 100% rename from test-notebooks/truncate_dataset.ipynb rename to notebooks/truncate_dataset.ipynb diff --git a/pdm.lock b/pdm.lock index 20f19b6..c916a9b 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,10 +2,13 @@ # It is not intended for manual editing. [metadata] -groups = ["default", "notebooks", "trials", "trails", "dev"] -strategy = ["cross_platform", "inherit_metadata"] -lock_version = "4.4.2" -content_hash = "sha256:a9f1cc71f6ee89d2f0572ef7254c9f0be702dbd1a4957b2f0d00d3b83ccc20d4" +groups = ["default", "dev", "notebooks", "trails", "trials"] +strategy = ["inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:468a23f2e765abd2cf8760a33a219a4e475f1ebc73630f792eddf6563293720a" + +[[metadata.targets]] +requires_python = ">=3.11" [[package]] name = "annotated-types" @@ -13,6 +16,9 @@ version = "0.6.0" requires_python = ">=3.8" summary = "Reusable constraint types to use with typing.Annotated" groups = ["default"] +dependencies = [ + "typing-extensions>=4.0.0; python_version < \"3.9\"", +] files = [ {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, @@ -25,8 +31,10 @@ requires_python = ">=3.8" summary = "High level compatibility layer for multiple asynchronous event loop implementations" groups = ["notebooks"] dependencies = [ + "exceptiongroup>=1.0.2; python_version < \"3.11\"", "idna>=2.8", "sniffio>=1.1", + "typing-extensions>=4.1; python_version < \"3.11\"", ] files = [ {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, @@ -53,6 +61,7 @@ summary = "Argon2 for Python" groups = ["notebooks"] dependencies = [ "argon2-cffi-bindings", + "typing-extensions; python_version < \"3.8\"", ] files = [ {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, @@ -114,6 +123,7 @@ summary = "Annotate AST trees with source code positions" groups = ["notebooks"] dependencies = [ "six>=1.12.0", + "typing; python_version < \"3.5\"", ] files = [ {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, @@ -126,6 +136,9 @@ version = "2.0.4" requires_python = ">=3.8" summary = "Simple LRU cache for asyncio" groups = ["notebooks"] +dependencies = [ + "typing-extensions>=4.0.0; python_version < \"3.11\"", +] files = [ {file = "async-lru-2.0.4.tar.gz", hash = "sha256:b8a59a5df60805ff63220b2a0c5b5393da5521b113cd5465a44eb037d81a5627"}, {file = "async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224"}, @@ -137,6 +150,9 @@ version = "23.2.0" requires_python = ">=3.7" summary = "Classes Without Boilerplate" groups = ["notebooks"] +dependencies = [ + "importlib-metadata; python_version < \"3.8\"", +] files = [ {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, @@ -148,6 +164,9 @@ version = "2.15.0" requires_python = ">=3.8" summary = "Internationalization utilities" groups = ["notebooks"] +dependencies = [ + "pytz>=2015.7; python_version < \"3.9\"", +] files = [ {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, @@ -210,6 +229,7 @@ version = "0.7.11" summary = "The Blis BLAS-like linear algebra library, as a self-contained C-extension." groups = ["default"] dependencies = [ + "numpy>=1.15.0; python_version < \"3.9\"", "numpy>=1.19.0; python_version >= \"3.9\"", ] files = [ @@ -232,6 +252,10 @@ version = "2.0.10" requires_python = ">=3.6" summary = "Super lightweight function registries for your library" groups = ["default"] +dependencies = [ + "typing-extensions>=3.6.4; python_version < \"3.8\"", + "zipp>=0.5; python_version < \"3.8\"", +] files = [ {file = "catalogue-2.0.10-py3-none-any.whl", hash = "sha256:58c2de0020aa90f4a2da7dfad161bf7b3b054c86a5f09fcedc0b2b740c109a9f"}, {file = "catalogue-2.0.10.tar.gz", hash = "sha256:4f56daa940913d3f09d589c191c74e5a6d51762b3a9e37dd53b7437afd6cda15"}, @@ -342,6 +366,7 @@ summary = "Composable command line interface toolkit" groups = ["default"] dependencies = [ "colorama; platform_system == \"Windows\"", + "importlib-metadata; python_version < \"3.8\"", ] files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, @@ -354,6 +379,10 @@ version = "0.16.0" requires_python = ">=3.7" summary = "pathlib-style classes for cloud storage services." groups = ["default"] +dependencies = [ + "importlib-metadata; python_version < \"3.8\"", + "typing-extensions>4; python_version < \"3.11\"", +] files = [ {file = "cloudpathlib-0.16.0-py3-none-any.whl", hash = "sha256:f46267556bf91f03db52b5df7a152548596a15aabca1c8731ef32b0b25a1a6a3"}, {file = "cloudpathlib-0.16.0.tar.gz", hash = "sha256:cdfcd35d46d529587d744154a0bdf962aca953b725c8784cd2ec478354ea63a3"}, @@ -417,6 +446,7 @@ groups = ["default"] dependencies = [ "pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4", "srsly<3.0.0,>=2.4.0", + "typing-extensions<4.5.0,>=3.7.4.1; python_version < \"3.8\"", ] files = [ {file = "confection-0.1.4-py3-none-any.whl", hash = "sha256:a658818d004939069c3e2b3db74a2cb9d956a5e61a1c9ad61788e0ee09a7090f"}, @@ -578,6 +608,17 @@ files = [ {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, ] +[[package]] +name = "et-xmlfile" +version = "1.1.0" +requires_python = ">=3.6" +summary = "An implementation of lxml.xmlfile for the standard library" +groups = ["dev"] +files = [ + {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, + {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, +] + [[package]] name = "executing" version = "2.0.1" @@ -621,6 +662,7 @@ dependencies = [ "Werkzeug>=3.0.0", "blinker>=1.6.2", "click>=8.1.3", + "importlib-metadata>=3.6.0; python_version < \"3.10\"", "itsdangerous>=2.1.2", ] files = [ @@ -634,6 +676,9 @@ version = "1.5.1" requires_python = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" summary = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" groups = ["notebooks"] +dependencies = [ + "cached-property>=1.3.0; python_version < \"3.8\"", +] files = [ {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, @@ -656,6 +701,9 @@ version = "0.14.0" requires_python = ">=3.7" summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" groups = ["notebooks"] +dependencies = [ + "typing-extensions; python_version < \"3.8\"", +] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -773,6 +821,7 @@ requires_python = ">=3.8" summary = "Read metadata from Python packages" groups = ["default"] dependencies = [ + "typing-extensions>=3.6.4; python_version < \"3.8\"", "zipp>=0.5", ] files = [ @@ -829,6 +878,7 @@ groups = ["notebooks"] dependencies = [ "colorama; sys_platform == \"win32\"", "decorator", + "exceptiongroup; python_version < \"3.11\"", "jedi>=0.16", "matplotlib-inline", "pexpect>4.3; sys_platform != \"win32\" and sys_platform != \"emscripten\"", @@ -955,7 +1005,9 @@ summary = "An implementation of JSON Schema validation for Python" groups = ["notebooks"] dependencies = [ "attrs>=22.2.0", + "importlib-resources>=1.4.0; python_version < \"3.9\"", "jsonschema-specifications>=2023.03.6", + "pkgutil-resolve-name>=1.3.10; python_version < \"3.9\"", "referencing>=0.28.4", "rpds-py>=0.7.1", ] @@ -971,6 +1023,7 @@ requires_python = ">=3.8" summary = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" groups = ["notebooks"] dependencies = [ + "importlib-resources>=1.4.0; python_version < \"3.9\"", "referencing>=0.31.0", ] files = [ @@ -1008,6 +1061,7 @@ requires_python = ">=3.8" summary = "Jupyter protocol implementation and client libraries" groups = ["notebooks"] dependencies = [ + "importlib-metadata>=4.8.3; python_version < \"3.10\"", "jupyter-core!=5.0.*,>=4.12", "python-dateutil>=2.8.2", "pyzmq>=23.0", @@ -1062,6 +1116,7 @@ requires_python = ">=3.8" summary = "Multi-Language Server WebSocket proxy for Jupyter Notebook/Lab server" groups = ["notebooks"] dependencies = [ + "importlib-metadata>=4.8.3; python_version < \"3.10\"", "jupyter-server>=1.1.2", ] files = [ @@ -1125,6 +1180,8 @@ groups = ["notebooks"] dependencies = [ "async-lru>=1.0.0", "httpx>=0.25.0", + "importlib-metadata>=4.8.3; python_version < \"3.10\"", + "importlib-resources>=1.4; python_version < \"3.9\"", "ipykernel>=6.5.0", "jinja2>=3.0.3", "jupyter-core", @@ -1134,6 +1191,7 @@ dependencies = [ "notebook-shim>=0.2", "packaging", "setuptools>=40.1.0", + "tomli>=1.2.2; python_version < \"3.11\"", "tornado>=6.2.0", "traitlets", ] @@ -1161,6 +1219,7 @@ summary = "A set of server components for JupyterLab and JupyterLab like applica groups = ["notebooks"] dependencies = [ "babel>=2.10", + "importlib-metadata>=4.8.3; python_version < \"3.10\"", "jinja2>=3.0.3", "json5>=0.9.0", "jsonschema>=4.18.0", @@ -1184,6 +1243,23 @@ files = [ {file = "jupyterlab_widgets-3.0.11.tar.gz", hash = "sha256:dd5ac679593c969af29c9bed054c24f26842baa51352114736756bc035deee27"}, ] +[[package]] +name = "kaleido" +version = "0.2.1" +summary = "Static image export for web-based visualization libraries with zero dependencies" +groups = ["default"] +dependencies = [ + "pathlib; python_version < \"3.4\"", +] +files = [ + {file = "kaleido-0.2.1-py2.py3-none-macosx_10_11_x86_64.whl", hash = "sha256:ca6f73e7ff00aaebf2843f73f1d3bacde1930ef5041093fe76b83a15785049a7"}, + {file = "kaleido-0.2.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:bb9a5d1f710357d5d432ee240ef6658a6d124c3e610935817b4b42da9c787c05"}, + {file = "kaleido-0.2.1-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:aa21cf1bf1c78f8fa50a9f7d45e1003c387bd3d6fe0a767cfbbf344b95bdc3a8"}, + {file = "kaleido-0.2.1-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:845819844c8082c9469d9c17e42621fbf85c2b237ef8a86ec8a8527f98b6512a"}, + {file = "kaleido-0.2.1-py2.py3-none-win32.whl", hash = "sha256:ecc72635860be616c6b7161807a65c0dbd9b90c6437ac96965831e2e24066552"}, + {file = "kaleido-0.2.1-py2.py3-none-win_amd64.whl", hash = "sha256:4670985f28913c2d063c5734d125ecc28e40810141bdb0a46f15b76c1d45f23c"}, +] + [[package]] name = "langcodes" version = "3.4.0" @@ -1399,6 +1475,7 @@ dependencies = [ "beautifulsoup4", "bleach!=5.0.0", "defusedxml", + "importlib-metadata>=3.6; python_version < \"3.10\"", "jinja2>=3.0", "jupyter-core>=4.7", "jupyterlab-pygments", @@ -1653,12 +1730,29 @@ files = [ {file = "nvidia_nvtx_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:65f4d98982b31b60026e0e6de73fbdfc09d08a96f4656dd3665ca616a11e1e82"}, ] +[[package]] +name = "openpyxl" +version = "3.1.5" +requires_python = ">=3.8" +summary = "A Python library to read/write Excel 2010 xlsx/xlsm files" +groups = ["dev"] +dependencies = [ + "et-xmlfile", +] +files = [ + {file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"}, + {file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"}, +] + [[package]] name = "overrides" version = "7.7.0" requires_python = ">=3.6" summary = "A decorator to automatically detect mismatch when overriding a method." groups = ["notebooks"] +dependencies = [ + "typing; python_version < \"3.5\"", +] files = [ {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, @@ -1682,6 +1776,7 @@ requires_python = ">=3.9" summary = "Powerful data structures for data analysis, time series, and statistics" groups = ["default"] dependencies = [ + "numpy>=1.22.4; python_version < \"3.11\"", "numpy>=1.23.2; python_version == \"3.11\"", "numpy>=1.26.0; python_version >= \"3.12\"", "python-dateutil>=2.8.2", @@ -2562,6 +2657,7 @@ dependencies = [ "jinja2", "langcodes<4.0.0,>=3.2.0", "murmurhash<1.1.0,>=0.28.0", + "numpy>=1.15.0; python_version < \"3.9\"", "numpy>=1.19.0; python_version >= \"3.9\"", "packaging>=20.0", "preshed<3.1.0,>=3.0.2", @@ -2654,6 +2750,8 @@ requires_python = ">=3.7" summary = "spaCy pipelines for pre-trained BERT and other transformers" groups = ["default"] dependencies = [ + "dataclasses<1.0,>=0.6; python_version < \"3.7\"", + "numpy>=1.15.0; python_version < \"3.9\"", "numpy>=1.19.0; python_version >= \"3.9\"", "spacy-alignments<1.0.0,>=0.7.2", "spacy<4.1.0,>=3.5.0", @@ -2812,14 +2910,18 @@ dependencies = [ "blis<0.8.0,>=0.7.8", "catalogue<2.1.0,>=2.0.4", "confection<1.0.0,>=0.0.1", + "contextvars<3,>=2.4; python_version < \"3.7\"", "cymem<2.1.0,>=2.0.2", + "dataclasses<1.0,>=0.6; python_version < \"3.7\"", "murmurhash<1.1.0,>=1.0.2", + "numpy>=1.15.0; python_version < \"3.9\"", "numpy>=1.19.0; python_version >= \"3.9\"", "packaging>=20.0", "preshed<3.1.0,>=3.0.2", "pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4", "setuptools", "srsly<3.0.0,>=2.4.0", + "typing-extensions<4.5.0,>=3.7.4.1; python_version < \"3.8\"", "wasabi<1.2.0,>=0.8.1", ] files = [ @@ -3133,6 +3235,7 @@ summary = "A lightweight console printing and formatting toolkit" groups = ["default"] dependencies = [ "colorama>=0.4.6; sys_platform == \"win32\" and python_version >= \"3.7\"", + "typing-extensions<4.5.0,>=3.7.4.1; python_version < \"3.8\"", ] files = [ {file = "wasabi-1.1.2-py3-none-any.whl", hash = "sha256:0a3f933c4bf0ed3f93071132c1b87549733256d6c8de6473c5f7ed2e171b5cf9"}, @@ -3144,6 +3247,9 @@ name = "wcwidth" version = "0.2.13" summary = "Measures the displayed width of unicode strings in a terminal" groups = ["notebooks"] +dependencies = [ + "backports-functools-lru-cache>=1.2.1; python_version < \"3.2\"", +] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, diff --git a/pyproject.toml b/pyproject.toml index 27cd55f..5a7bb6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ dependencies = [ "dash>=2.17.0", "dash-cytoscape>=1.0.1", "py4cytoscape>=1.9.0", + "kaleido==0.2.1", ] requires-python = ">=3.11" readme = "README.md" @@ -44,6 +45,7 @@ trials = [ ] dev = [ "cython>=3.0.10", + "openpyxl>=3.1.5", ] [tool.ruff] diff --git a/scripts/dash_timeline_static.py b/scripts/dash_timeline_static.py index 6a04ae6..5dc2372 100644 --- a/scripts/dash_timeline_static.py +++ b/scripts/dash_timeline_static.py @@ -3,11 +3,11 @@ import webbrowser from collections.abc import Collection, Iterable from threading import Thread from typing import Any, Final, cast - -import pandas as pd +from pathlib import Path # import dash_cytoscape as cyto import plotly.express as px +import plotly.io from dash import ( Dash, Input, @@ -22,16 +22,17 @@ from pandas import DataFrame from plotly.graph_objects import Figure import lang_main.io +from lang_main import model_loader as m_load from lang_main.analysis import graphs, tokens from lang_main.analysis.timeline import ( calc_delta_to_next_failure, filter_timeline_cands, ) from lang_main.constants import ( + MODEL_LOADER_MAP, NAME_DELTA_FEAT_TO_NEXT_FAILURE, NAME_DELTA_FEAT_TO_REPAIR, SAVE_PATH_FOLDER, - SPCY_MODEL, ) from lang_main.errors import EmptyEdgesError, EmptyGraphError from lang_main.pipelines.predefined import ( @@ -43,10 +44,16 @@ from lang_main.types import ( EntryPoints, HTMLColumns, HTMLTable, + LanguageModels, ObjectID, TimelineCandidates, ) +# ** model +SPACY_MODEL = m_load.instantiate_model( + model_load_map=MODEL_LOADER_MAP, + model=LanguageModels.SPACY, +) # ** data # p_df = Path(r'../results/test_20240619/TIMELINE.pkl').resolve() p_df = lang_main.io.get_entry_point(SAVE_PATH_FOLDER, EntryPoints.TIMELINE_POST) @@ -62,17 +69,42 @@ rescaling_pipe = build_tk_graph_rescaling_pipe( exit_point=EntryPoints.TIMELINE_TK_GRAPH_RESCALED, save_result=False, ) -BASE_NETWORK_NAME: Final[str] = 'test_timeline' +BASE_NETWORK_NAME: Final[str] = 'timeline_candidates' # RENDER_FOLDER: Final[Path] = Path.cwd() / 'assets/' graph_render_pipe = build_tk_graph_render_pipe( with_subgraphs=False, base_network_name=BASE_NETWORK_NAME, ) # PTH_RENDERED_GRAPH = f'assets/{BASE_NETWORK_NAME}.svg' +PTH_RENDERED_TIMELINE = lang_main.io.get_entry_point( + SAVE_PATH_FOLDER, + 'chart_timeline', + file_ext='.svg', + check_existence=False, +) +PTH_TABLE_TIMELINE = lang_main.io.get_entry_point( + SAVE_PATH_FOLDER, + 'table_timeline', + file_ext='.xlsx', + check_existence=False, +) +PTH_RENDERED_DELTA_REPAIR = lang_main.io.get_entry_point( + SAVE_PATH_FOLDER, + 'chart_delta_repair', + file_ext='.svg', + check_existence=False, +) +PTH_TABLE_DELTA_REPAIR = lang_main.io.get_entry_point( + SAVE_PATH_FOLDER, + 'table_delta_repair', + file_ext='.xlsx', + check_existence=False, +) PTH_RENDERED_GRAPH = lang_main.io.get_entry_point( SAVE_PATH_FOLDER, BASE_NETWORK_NAME, file_ext='.svg', + check_existence=False, ) # NAME_DELTA_FEAT_TO_NEXT_FAILURE: Final[str] = 'Zeitspanne bis zum nächsten Ereignis [Tage]' @@ -168,7 +200,7 @@ graph_layout = html.Div( id='static-graph-img', alt='static rendered graph', style={ - 'width': 'auto', + 'width': '900px', 'height': 'auto', }, ), @@ -212,7 +244,27 @@ app.layout = html.Div( children=[ html.H3(id='object-text'), dcc.Dropdown(id='selector-candidates'), + html.Button( + 'Download Diagramm', + id='bt-dl-timeline', + style={ + 'marginLeft': 'auto', + 'width': '300px', + 'marginTop': '1em', + }, + ), + dcc.Download(id='dl-timeline'), dcc.Graph(id='figure-occurrences'), + html.Button( + 'Download Diagramm', + id='bt-dl-deltarepair', + style={ + 'marginLeft': 'auto', + 'width': '300px', + 'marginTop': '1em', + }, + ), + dcc.Download(id='dl-deltarepair'), dcc.Graph(id='figure-delta'), ] ), @@ -221,6 +273,16 @@ app.layout = html.Div( html.Div( [ html.H5('Überblick ähnlicher Vorgänge'), + dcc.Download(id='dl-table-timeline'), + html.Button( + 'Download Table', + id='bt-table-timeline', + style={ + 'marginLeft': 'auto', + 'width': '300px', + 'marginTop': '1em', + }, + ), dash_table.DataTable(id='table-candidates'), ], style={'paddingBottom': '1em'}, @@ -233,6 +295,16 @@ app.layout = html.Div( 'bis zum nächsten Ereignis' ) ), + dcc.Download(id='dl-table-deltarepair'), + html.Button( + 'Download Table', + id='bt-table-deltarepair', + style={ + 'marginLeft': 'auto', + 'width': '300px', + 'marginTop': '1em', + }, + ), dash_table.DataTable(id='table-best-actions'), ] ), @@ -368,6 +440,7 @@ def transform_to_HTML_table( date_cols: Iterable[str] | None = None, sorting_feature: str | None = None, sorting_ascending: bool = True, + save_path: Path | None = None, ) -> tuple[HTMLColumns, HTMLTable]: target_features = list(target_features) data = data.copy() @@ -383,6 +456,9 @@ def transform_to_HTML_table( columns = [{'name': col, 'id': col} for col in data.columns] table_data = data.to_dict('records') + if save_path is not None: + data.to_excel(save_path) + return columns, table_data @@ -410,6 +486,7 @@ def update_tables_candidates( date_cols=TABLE_FEATS_DATES, sorting_feature='ErstellungsDatum', sorting_ascending=True, + save_path=PTH_TABLE_TIMELINE, ) # df = df.filter(items=TABLE_FEATS_OVERVIEW, axis=1).sort_values( # by='ErstellungsDatum', ascending=True @@ -430,6 +507,7 @@ def update_tables_candidates( data=cands_best_actions, target_features=TABLE_FEATS_BEST_ACTIONS, date_cols=TABLE_FEATS_DATES, + save_path=PTH_TABLE_DELTA_REPAIR, ) return overview_cols, overview_table, best_actions_cols, best_actions_table @@ -457,7 +535,7 @@ def display_candidates_as_graph(index, obj_id): t1 = time.perf_counter() tk_graph_cands, _ = tokens.build_token_graph( data=df, - model=SPCY_MODEL, + model=SPACY_MODEL, target_feature='VorgangsBeschreibung', build_map=False, logging_graph=False, @@ -496,10 +574,58 @@ def display_candidates_as_graph(index, obj_id): Input('bt-reset', 'n_clicks'), prevent_initial_call=True, ) -def func(n_clicks): +def download_graph(_): return dcc.send_file(path=PTH_RENDERED_GRAPH) +@callback( + Output('dl-timeline', 'data'), + Input('bt-dl-timeline', 'n_clicks'), + State('figure-occurrences', 'figure'), + prevent_initial_call=True, +) +def download_timeline(_, fig: dict): + # add these lines before fig = go.Figure(fig_raw) + if 'rangeslider' in fig['layout']['xaxis']: + del fig['layout']['xaxis']['rangeslider']['yaxis'] + figure = Figure(fig) + figure.write_image(PTH_RENDERED_TIMELINE) + return dcc.send_file(path=PTH_RENDERED_TIMELINE) + + +@callback( + Output('dl-deltarepair', 'data'), + Input('bt-dl-deltarepair', 'n_clicks'), + State('figure-delta', 'figure'), + prevent_initial_call=True, +) +def download_delta_repair(_, fig: dict): + # add these lines before fig = go.Figure(fig_raw) + if 'rangeslider' in fig['layout']['xaxis']: + del fig['layout']['xaxis']['rangeslider']['yaxis'] + figure = Figure(fig) + figure.write_image(PTH_RENDERED_DELTA_REPAIR) + return dcc.send_file(path=PTH_RENDERED_DELTA_REPAIR) + + +@callback( + Output('dl-table-timeline', 'data'), + Input('bt-table-timeline', 'n_clicks'), + prevent_initial_call=True, +) +def download_table_timeline(_): + return dcc.send_file(path=PTH_TABLE_TIMELINE) + + +@callback( + Output('dl-table-deltarepair', 'data'), + Input('bt-table-deltarepair', 'n_clicks'), + prevent_initial_call=True, +) +def download_table_delta_repair(_): + return dcc.send_file(path=PTH_TABLE_DELTA_REPAIR) + + def _start_webbrowser(): host = '127.0.0.1' port = '8050' diff --git a/scripts/lang_main_config.toml b/scripts/lang_main_config.toml index 14699c6..77bd396 100644 --- a/scripts/lang_main_config.toml +++ b/scripts/lang_main_config.toml @@ -2,8 +2,10 @@ [paths] inputs = './inputs/' -results = './results/test_20240619/' -dataset = '../data/02_202307/Export4.csv' +results = './results/dummy_N_1000/' +dataset = '../data/Dummy_Dataset_N_1000.csv' +# results = './results/test_20240807/' +# dataset = '../data/02_202307/Export4.csv' #results = './results/Export7/' #dataset = './01_03_Rohdaten_202403/Export7_59499_Zeilen.csv' #results = './results/Export7_trunc/' @@ -12,12 +14,12 @@ dataset = '../data/02_202307/Export4.csv' # only debugging features, production-ready pipelines should always # be fully executed [control] -preprocessing_skip = true -token_analysis_skip = true -graph_postprocessing_skip = true -graph_rescaling_skip = true +preprocessing_skip = false +token_analysis_skip = false +graph_postprocessing_skip = false +graph_rescaling_skip = false graph_static_rendering_skip = false -time_analysis_skip = true +time_analysis_skip = false #[export_filenames] #filename_cossim_filter_candidates = 'CosSim-FilterCandidates' @@ -34,7 +36,7 @@ threshold_amount_characters = 5 threshold_similarity = 0.8 [graph_postprocessing] -threshold_edge_weight = 150 +threshold_edge_weight = 1 [time_analysis.uniqueness] threshold_unique_texts = 4 diff --git a/src/lang_main/__init__.py b/src/lang_main/__init__.py index 6d1d346..f10c95c 100644 --- a/src/lang_main/__init__.py +++ b/src/lang_main/__init__.py @@ -18,7 +18,7 @@ p4c.py4cytoscape_logger.detail_logger.addHandler(logging.NullHandler()) # ** lang-main config logging.Formatter.converter = gmtime -LOG_FMT: Final[str] = '%(asctime)s | %(module)s:%(levelname)s | %(message)s' +LOG_FMT: Final[str] = '%(asctime)s | lang_main:%(module)s:%(levelname)s | %(message)s' LOG_DATE_FMT: Final[str] = '%Y-%m-%d %H:%M:%S +0000' logging.basicConfig( stream=sys.stdout, diff --git a/src/lang_main/analysis/preprocessing.py b/src/lang_main/analysis/preprocessing.py index 9130bb1..b5a3eea 100644 --- a/src/lang_main/analysis/preprocessing.py +++ b/src/lang_main/analysis/preprocessing.py @@ -70,7 +70,7 @@ def load_raw_data( filepath_or_buffer=path, sep=';', encoding='cp1252', - parse_dates=date_cols, + parse_dates=list(date_cols), dayfirst=True, ) logger.info('Loaded dataset successfully.') @@ -278,7 +278,8 @@ def merge_similarity_dupl( return (merged_data,) -##################################################################### +# ** ################################################################################# +# TODO check removal def build_embedding_map( data: Series, model: GermanSpacyModel | SentenceTransformer, diff --git a/src/lang_main/analysis/timeline.py b/src/lang_main/analysis/timeline.py index 4e339a0..9339a7d 100644 --- a/src/lang_main/analysis/timeline.py +++ b/src/lang_main/analysis/timeline.py @@ -8,10 +8,13 @@ from tqdm.auto import tqdm # TODO: check deletion from lang_main.analysis.shared import ( candidates_by_index, entry_wise_cleansing, - pattern_escape_seq_sentences, similar_index_connection_graph, similar_index_groups, ) +from lang_main.constants import ( + NAME_DELTA_FEAT_TO_NEXT_FAILURE, + NAME_DELTA_FEAT_TO_REPAIR, +) from lang_main.loggers import logger_timeline as logger from lang_main.types import ( DataFrameTLFiltered, @@ -94,7 +97,7 @@ def calc_delta_to_repair( data: DataFrame, date_feature_start: str = 'ErstellungsDatum', date_feature_end: str = 'ErledigungsDatum', - name_delta_feature: str = 'delta_to_repair', + name_delta_feature: str = NAME_DELTA_FEAT_TO_REPAIR, convert_to_days: bool = True, ) -> tuple[DataFrame]: logger.info('Calculating time differences between start and end of operations...') @@ -316,7 +319,7 @@ def filter_timeline_cands( def calc_delta_to_next_failure( data: DataFrameTLFiltered, date_feature: str = 'ErstellungsDatum', - name_delta_feature: str = 'delta_to_next_failure', + name_delta_feature: str = NAME_DELTA_FEAT_TO_NEXT_FAILURE, convert_to_days: bool = True, ) -> DataFrameTLFiltered: data = data.copy() diff --git a/src/lang_main/analysis/tokens.py b/src/lang_main/analysis/tokens.py index aaf0534..7e6dedf 100644 --- a/src/lang_main/analysis/tokens.py +++ b/src/lang_main/analysis/tokens.py @@ -5,9 +5,6 @@ from typing import Literal, cast, overload from dateutil.parser import parse from pandas import DataFrame -from spacy.language import Language as GermanSpacyModel -from spacy.tokens.doc import Doc as SpacyDoc -from spacy.tokens.token import Token as SpacyToken from tqdm.auto import tqdm from lang_main.analysis.graphs import ( @@ -15,7 +12,12 @@ from lang_main.analysis.graphs import ( update_graph, ) from lang_main.loggers import logger_token_analysis as logger -from lang_main.types import PandasIndex +from lang_main.types import ( + PandasIndex, + SpacyDoc, + SpacyModel, + SpacyToken, +) # ** POS # POS_OF_INTEREST: frozenset[str] = frozenset(['NOUN', 'PROPN', 'ADJ', 'VERB', 'AUX']) @@ -147,7 +149,7 @@ def add_doc_info_to_graph( @overload def build_token_graph( data: DataFrame, - model: GermanSpacyModel, + model: SpacyModel, *, target_feature: str = ..., weights_feature: str | None = ..., @@ -161,7 +163,7 @@ def build_token_graph( @overload def build_token_graph( data: DataFrame, - model: GermanSpacyModel, + model: SpacyModel, *, target_feature: str = ..., weights_feature: str | None = ..., @@ -174,7 +176,7 @@ def build_token_graph( def build_token_graph( data: DataFrame, - model: GermanSpacyModel, + model: SpacyModel, *, target_feature: str = 'entry', weights_feature: str | None = None, @@ -233,7 +235,7 @@ def build_token_graph( def build_token_graph_simple( data: DataFrame, - model: GermanSpacyModel, + model: SpacyModel, ) -> tuple[TokenGraph, dict[PandasIndex, SpacyDoc]]: graph = TokenGraph() model_input = cast(tuple[str], tuple(data['entry'].to_list())) @@ -264,7 +266,7 @@ def build_token_graph_simple( def build_token_graph_old( data: DataFrame, - model: GermanSpacyModel, + model: SpacyModel, ) -> tuple[TokenGraph]: # empty NetworkX directed graph # graph = nx.DiGraph() diff --git a/src/lang_main/constants.py b/src/lang_main/constants.py index 9d0e52b..88a789c 100644 --- a/src/lang_main/constants.py +++ b/src/lang_main/constants.py @@ -1,12 +1,19 @@ from pathlib import Path from typing import Final -import spacy -from sentence_transformers import SentenceTransformer -from spacy.language import Language as GermanSpacyModel - +# TODO check removal +# import spacy +# from sentence_transformers import SentenceTransformer +# from spacy.language import Language as GermanSpacyModel from lang_main import CONFIG, CYTO_PATH_STYLESHEET -from lang_main.types import CytoLayoutProperties, CytoLayouts, STFRDeviceTypes +from lang_main import model_loader as m_load +from lang_main.types import ( + CytoLayoutProperties, + CytoLayouts, + LanguageModels, + ModelLoaderMap, + STFRDeviceTypes, +) __all__ = [ 'CONFIG', @@ -38,14 +45,33 @@ SKIP_TIME_ANALYSIS: Final[bool] = CONFIG['control']['time_analysis_skip'] # ** models -# ** sentence_transformers +# ** loading +SPACY_MODEL_NAME: Final[str] = 'de_dep_news_trf' +STFR_MODEL_NAME: Final[str] = 'sentence-transformers/all-mpnet-base-v2' STFR_DEVICE: Final[STFRDeviceTypes] = STFRDeviceTypes.CPU -STFR_MODEL: Final[SentenceTransformer] = SentenceTransformer( - 'sentence-transformers/all-mpnet-base-v2', device=STFR_DEVICE -) +MODEL_LOADER_MAP: Final[ModelLoaderMap] = { + LanguageModels.SENTENCE_TRANSFORMER: { + 'func': m_load.load_sentence_transformer, + 'kwargs': { + 'model_name': STFR_MODEL_NAME, + 'device': STFR_DEVICE, + }, + }, + LanguageModels.SPACY: { + 'func': m_load.load_spacy, + 'kwargs': { + 'model_name': SPACY_MODEL_NAME, + }, + }, +} +# ** sentence_transformers + +# STFR_MODEL: Final[SentenceTransformer] = SentenceTransformer( +# 'sentence-transformers/all-mpnet-base-v2', device=STFR_DEVICE +# ) # ** spacy -SPCY_MODEL: Final[GermanSpacyModel] = spacy.load('de_dep_news_trf') +# SPCY_MODEL: Final[GermanSpacyModel] = spacy.load('de_dep_news_trf') # ** export # ** preprocessing @@ -82,6 +108,7 @@ CYTO_STYLESHEET_NAME: Final[str] = 'lang_main' CYTO_SELECTION_PROPERTY: Final[str] = 'node_selection' CYTO_NUMBER_SUBGRAPHS: Final[int] = 5 CYTO_ITER_NEIGHBOUR_DEPTH: Final[int] = 2 +CYTO_NETWORK_ZOOM_FACTOR: Final[float] = 0.96 # ** time_analysis.uniqueness THRESHOLD_UNIQUE_TEXTS: Final[int] = CONFIG['time_analysis']['uniqueness'][ diff --git a/src/lang_main/cytoscape_config/template_test.cys b/src/lang_main/cytoscape_config/template_test.cys index d245d1e18df5f0914a0f52d351057873bc6f2488..8cb52592ba81c57525dc7559a11eb9fe39f88a94 100644 GIT binary patch delta 24917 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 A{{R30 delta 25260 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 diff --git a/src/lang_main/io.py b/src/lang_main/io.py index 21322cd..402323e 100644 --- a/src/lang_main/io.py +++ b/src/lang_main/io.py @@ -93,9 +93,10 @@ def get_entry_point( saving_path: Path, filename: str, file_ext: str = '.pkl', + check_existence: bool = True, ) -> Path: entry_point_path = (saving_path / filename).with_suffix(file_ext) - if not entry_point_path.exists(): + if check_existence and not entry_point_path.exists(): raise FileNotFoundError( f'Could not find provided entry data under path: >>{entry_point_path}<<' ) diff --git a/src/lang_main/model_loader.py b/src/lang_main/model_loader.py new file mode 100644 index 0000000..fcac638 --- /dev/null +++ b/src/lang_main/model_loader.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from typing import Literal, overload + +import spacy +from sentence_transformers import SentenceTransformer + +from lang_main.types import ( + LanguageModels, + Model, + ModelLoaderMap, + SpacyModel, + STFRDeviceTypes, +) + + +@overload +def instantiate_model( + model_load_map: ModelLoaderMap, + model: Literal[LanguageModels.SENTENCE_TRANSFORMER], +) -> SentenceTransformer: ... + + +@overload +def instantiate_model( + model_load_map: ModelLoaderMap, + model: Literal[LanguageModels.SPACY], +) -> SpacyModel: ... + + +def instantiate_model( + model_load_map: ModelLoaderMap, + model: LanguageModels, +) -> Model: + if model not in model_load_map: + raise KeyError(f'Model >>{model}<< not known. Choose from: {model_load_map.keys()}') + builder_func = model_load_map[model]['func'] + func_kwargs = model_load_map[model]['kwargs'] + + return builder_func(**func_kwargs) + + +def load_spacy( + model_name: str, +) -> SpacyModel: + return spacy.load(model_name) + + +def load_sentence_transformer( + model_name: str, + device: STFRDeviceTypes, +) -> SentenceTransformer: + return SentenceTransformer(model_name_or_path=model_name, device=device) diff --git a/src/lang_main/pipelines/predefined.py b/src/lang_main/pipelines/predefined.py index f4d404f..c074ebe 100644 --- a/src/lang_main/pipelines/predefined.py +++ b/src/lang_main/pipelines/predefined.py @@ -1,5 +1,6 @@ from pathlib import Path +from lang_main import model_loader as m_load from lang_main.analysis import graphs from lang_main.analysis.preprocessing import ( analyse_feature, @@ -29,10 +30,9 @@ from lang_main.constants import ( DATE_COLS, FEATURE_NAME_OBJ_ID, MODEL_INPUT_FEATURES, + MODEL_LOADER_MAP, NAME_DELTA_FEAT_TO_REPAIR, SAVE_PATH_FOLDER, - SPCY_MODEL, - STFR_MODEL, THRESHOLD_AMOUNT_CHARACTERS, THRESHOLD_EDGE_WEIGHT, THRESHOLD_NUM_ACTIVITIES, @@ -43,7 +43,18 @@ from lang_main.constants import ( ) from lang_main.pipelines.base import Pipeline from lang_main.render import cytoscape as cyto -from lang_main.types import EntryPoints +from lang_main.types import EntryPoints, LanguageModels + +# ** Models +STFR_MODEL = m_load.instantiate_model( + model_load_map=MODEL_LOADER_MAP, + model=LanguageModels.SENTENCE_TRANSFORMER, +) + +SPACY_MODEL = m_load.instantiate_model( + model_load_map=MODEL_LOADER_MAP, + model=LanguageModels.SPACY, +) # ** pipeline configuration @@ -61,7 +72,7 @@ def build_base_target_feature_pipe() -> Pipeline: pipe_target_feat.add( entry_wise_cleansing, { - 'target_feature': ('VorgangsBeschreibung',), + 'target_features': ('VorgangsBeschreibung',), 'cleansing_func': clean_string_slim, }, save_result=True, @@ -106,7 +117,6 @@ def build_base_target_feature_pipe() -> Pipeline: # ** Merge duplicates def build_merge_duplicates_pipe() -> Pipeline: pipe_merge = Pipeline(name='Merge_Duplicates', working_dir=SAVE_PATH_FOLDER) - # pipe_merge.add(merge_similarity_dupl, save_result=True) pipe_merge.add( numeric_pre_filter_feature, { @@ -134,7 +144,7 @@ def build_tk_graph_pipe() -> Pipeline: pipe_token_analysis.add( build_token_graph, { - 'model': SPCY_MODEL, + 'model': SPACY_MODEL, 'target_feature': 'entry', 'weights_feature': 'num_occur', 'batch_idx_feature': 'batched_idxs', diff --git a/src/lang_main/render/cytoscape.py b/src/lang_main/render/cytoscape.py index d5201a9..f6a83dd 100644 --- a/src/lang_main/render/cytoscape.py +++ b/src/lang_main/render/cytoscape.py @@ -14,6 +14,7 @@ from lang_main.constants import ( CYTO_ITER_NEIGHBOUR_DEPTH, CYTO_LAYOUT_NAME, CYTO_LAYOUT_PROPERTIES, + CYTO_NETWORK_ZOOM_FACTOR, CYTO_NUMBER_SUBGRAPHS, CYTO_PATH_STYLESHEET, CYTO_SANDBOX_NAME, @@ -125,6 +126,17 @@ def reset_current_network_to_base() -> None: p4c.set_current_network(CYTO_BASE_NETWORK_NAME) +def fit_content( + zoom_factor: float = CYTO_NETWORK_ZOOM_FACTOR, + network_name: str = CYTO_BASE_NETWORK_NAME, +) -> None: + p4c.hide_all_panels() + p4c.fit_content(selected_only=False, network=network_name) + zoom_current = p4c.get_network_zoom(network=network_name) + zoom_new = zoom_current * zoom_factor + p4c.set_network_zoom_bypass(zoom_new, bypass=False, network=network_name) + + def export_network_to_image( filename: str, target_folder: Path = SAVE_PATH_FOLDER, @@ -156,9 +168,10 @@ def export_network_to_image( if filetype == 'SVG': text_as_font = False + # close non-necessary windows and fit graph in frame before image display + fit_content(network_name=network_name) # image is generated in sandbox directory and transferred to target destination # (preparation for remote instances of Cytoscape) - # TODO close non-necessary windows before image display p4c.export_image( filename=filename, type=filetype, @@ -168,7 +181,6 @@ def export_network_to_image( export_text_as_font=text_as_font, page_size=pdf_export_page_size, ) - # TODO change back to Cytoscape 3.10 and above # TODO remove if Cytoscape >= 3.10.* is running in container # p4c.export_image( # filename=filename, @@ -211,7 +223,7 @@ def layout_network( logger.debug('Applying layout to network...') p4c.set_layout_properties(layout_name, layout_properties) p4c.layout_network(layout_name=layout_name, network=network_name) - p4c.fit_content(selected_only=False, network=network_name) + fit_content(network_name=network_name) logger.debug('Layout application to network successful.') @@ -245,7 +257,7 @@ def apply_style_to_network( """ logger.debug('Applying style to network...') styles_avail = cast(list[str], p4c.get_visual_style_names()) - if CYTO_STYLESHEET_NAME not in styles_avail: + if style_name not in styles_avail: if not pth_to_stylesheet.exists(): # existence for standard path verified at import, but not for other # provided paths @@ -278,7 +290,7 @@ def apply_style_to_network( node_size_property, number_scheme=scheme, mapping_type='c', - style_name='lang_main', + style_name=style_name, default_number=min_node_size, ) p4c.set_node_size_mapping(**node_size_map) @@ -289,7 +301,7 @@ def apply_style_to_network( # p4c.set_node_size_bypass(nodes_SUID, new_sizes=min_node_size, network=network_name) # p4c.set_visual_style(style_name, network=network_name) # time.sleep(1) # if not waited image export could be without applied style - p4c.fit_content(selected_only=False, network=network_name) + fit_content(network_name=network_name) logger.debug('Style application to network successful.') @@ -384,7 +396,7 @@ def make_subnetwork( network=network_name, ) p4c.set_current_network(subnetwork_name) - p4c.fit_content(selected_only=False, network=subnetwork_name) + if export_image: time.sleep(1) export_network_to_image( diff --git a/src/lang_main/types.py b/src/lang_main/types.py index 3e7f21b..ebd5c60 100644 --- a/src/lang_main/types.py +++ b/src/lang_main/types.py @@ -1,5 +1,5 @@ import enum -from collections.abc import Hashable +from collections.abc import Callable, Hashable from typing import ( Any, Literal, @@ -10,9 +10,20 @@ from typing import ( import numpy as np from pandas import DataFrame +from sentence_transformers import SentenceTransformer +from spacy.language import Language as SpacyModel from spacy.tokens.doc import Doc as SpacyDoc +from spacy.tokens.token import Token as SpacyToken from torch import Tensor +__all__ = [ + 'SentenceTransformer', + 'SpacyModel', + 'SpacyDoc', + 'SpacyToken', + 'Tensor', +] + # ** logging class LoggingLevels(enum.IntEnum): @@ -23,6 +34,24 @@ class LoggingLevels(enum.IntEnum): CRITICAL = 50 +# ** models +class LanguageModels(enum.StrEnum): + SENTENCE_TRANSFORMER = enum.auto() + SPACY = enum.auto() + + +Model: TypeAlias = SentenceTransformer | SpacyModel +ModelLoaderFunc: TypeAlias = Callable[..., Model] + + +class ModelLoaderInfo(TypedDict): + func: ModelLoaderFunc + kwargs: dict[str, Any] + + +ModelLoaderMap: TypeAlias = dict[LanguageModels, ModelLoaderInfo] + + # ** devices class STFRDeviceTypes(enum.StrEnum): CPU = enum.auto() diff --git a/test-notebooks/image.png b/test-notebooks/image.png deleted file mode 100644 index c8863fd55b3f372065974acc2dea1c3606a58222..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54002 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