Celem tego projektu jest utworzenie otwartej i dobrze udokumentowanej biblioteki sigman pozwalającej na dowolną analizę danych biomedycznych w formie wielokanałowych sygnałów cyfrowych za pomocą prostych do utworzenia zewnętrznych procedur, a także aplikacji QtSigman która pozwoli na korzystanie z tej biblioteki przez interfejs graficzny.
- Python 3 - całość
- NumPy - podstawowa obsługa danych
- SciPy - filtry/dodatkowe funkcje do analizy
- matplotlib - wizualizacja wykresów
- PyQt5 - GUI
- XlsxWriter - Zapisywanie plików .xlsx
git clone "https://github.com/k-cybulski/sigman-project.git"
sudo apt install python3-pip
pip3 install PyQt5 numpy scipy matplotlib XlsxWriter
Należy uruchomić skrypt run_qtsigman.py
.
Poniższe przykłady zakładają importowanie sigman
jako sm
, a sigman.file_manager
jako fm
.
Podstawowym rodzajem danych w bibliotece sigman jest przebieg sigman.Wave
(z ang. waveform) . Określony jest on przede wszystkim tablicą danych Wave.data
oraz całkowitym czasem trwania Wave.complete_length
. Z liczby danych oraz ich długości w czasie obliczana jest częstotliwość samplowania Wave.sample_rate
, oraz długość sampla Wave.sample_length
. Do późniejszej analizy ważny będzie także typ przebiegu Wave.type
określający rodzaj danych, np. ecg
czy bp
.
Inicjalizacja sigman.Wave
zawierającego wartości funkcji sinus od 0 do 4pi na umownej przestrzeni 10 sekund.
import sigman as sm
import numpy as np
sine = np.sin(np.linspace(0, 4*np.pi))
sine_wave = sm.Wave(sine, 10, 'sine')
Obiekty sigman.Wave
można importować za pomocą funkcji file_manager.import_wave
.
from sigman import file_manager as fm
ecg = fm.import_wave('example_data/EKG.dat', 'ecg')
W wypadku rozsynchronizowania przebiegu względem innych w czasie ważna jest też zmienna Wave.offset
pozwalająca korygować takie błędy przez przesuwanie przebiegu.
Podstawową metodą pobierania informacji z sigman.Wave
jest Wave.data_slice
, która zwraca tablicę wartości danych na danym zakresie czasowywm. Dokładniejsza dokumentacja w sigman/__init__.py
. Kilka przykładów:
>>> ecg.data_slice(5, 5.025) # 25 milisekundowy wycinek przebiegu
array([ 0.10659864, 0.10404629, 0.1673287 , 0.1688633 , 0.04704312])
>>> ecg.data_slice(5, 8, value_every=1) # wycinek wartości przebiegu na przestrzeni 3 sekund oddalonych o 1 sekundę od siebie
array([ 0.10659864, -0.52108215, 0.95624742])
>>> ecg.data_slice(5, 25, value_count=5) # wycinek 5 wartości na przestrzeni 20 sekund
array([ 0.10659864, 0.35472794, -0.61547362, -0.75704451, -0.59674523])
Punkty oznaczające wydarzenia w czasie o danej wartości, np. R na przebiegu EKG, symbolizowane są klasą sigman.Points
. Zawiera on dwie tablice Points.data_x
oraz Points.data_y
posortowane w kolejności data_x
, a także typ punktów jak r
.
Inicjalizacja kilku punktów.
import sigman as sm
data_x = [1, 4, 7]
data_y = [3, -2, 4]
points = sm.Points(data_x, data_y, 'example')
Punkty można importować
from sigman import file_manager as fm
r = fm.import_points('example_data/R.dat', 'r')
lub odnajdywać za pomocą procedur, co zostanie wytłumaczone dogłębniej potem.
Podobnie jak przy sigman.Wave
jest metoda Points.data_slice
.
>>> r.data_slice(20,23)
(array([ 20.61868, 21.49193, 22.3552 ]), array([ 4.07120371, 3.76066208, 3.69650602]))
Klasa zawierająca tablice obliczonych wartości Parameter.values
parametru, np. częstotliwości bicia serca, a także informacje o tym od jakiego czasu Parameter.begin_times
do jakiego Parameter.end_times
są one obliczone. Tworzy się je tylko wykorzystując procedury.
Klasa łącząca kilka powyższych danych w jedną spójną całość. Może zawierać nieokreśloną liczbę sigman.Wave
, sigman.Points
oraz sigman.Parameter
w trzech dict
. Pozwala dzięki temu stosować procedury korzystające z kilku kanałów danych, np. procedurę odnajdującą wcięcia dykrotyczne w oparciu o SBP i przebiegi BP oraz EKG. Procedury pobierają dane przez bezpośrednie odwołania do dict
danych, np. composite_data.waves['ecg']
. Te trzy dict
to:
Composite_data.waves
Composite_data.points
Composite_data.parameters
Dodawać je można i przy inicjalizacji i już po zainicjalizowaniu.
import sigman as sm
from sigman import file_manager as fm
ecg = fm.import_wave('example_data/EKG.dat', 'ecg')
composite_data = sm.Composite_data(waves={'ecg':ecg}) # przy inicjalizacji
bp = fm.import_wave('example_data/BP.dat', 'bp')
composite_data.add_wave(bp, 'bp') # po inicjalizacji
r = fm.import_points('example_data/R.dat', 'r')
composite_data.add_points(r, 'r')
file_manager
zawiera też funkcje pozwalające zapisywać sigman.Composite_data
na potem.
fm.save_composite_data('temporary_save.pickle', composite_data)
oraz
composite_data = fm.load_composite_data('temporary_save.pickle')
Obecnie wykorzystuje to moduł pythonowy pickle
, choć jest w planach w przyszłości zamienić go na coś bezpieczniejszego.
Biblioteka sigman zawiera moduł visualizer
pozwalający na bardzo proste lecz dość ograniczone wizualizowanie sigman.Composite_data
. Po wykonaniu powyższego kodu tworzącego composite_data można spróbować:
from sigman import visualizer as vis
vis.visualize_composite_data(composite_data)
visualizer
pozwala także na wizualizację tylko wycinka czasowego, czy też tylko wybranych danych z sigman.Composite_data
.
vis.visualize_composite_data(composite_data, begin_time=40, end_time=60,
wanted_waves=['ecg'], title="Wykres EKG")
Moduł analyzer
pozwala na stosowanie zewnętrznych procedur z folderu sigman-project/procedures
na danych. Ich dokładna struktura i całokształt opisany jest głębiej w samym pliku modułu.
Zakładany sposób wykorzystania procedur polega na zaimportowaniu ich funkcją analyzer.import_procedure
, zmodyfikowaniu wybranych argumentów z procedure.default_arguments
, zaaplikowaniu jej przez odpowiednią funkcję z sigman.analyzer
a następnie zamiany danych w sigman.Composite_data
na nowe.
Filtrację / modyfikację przebiegu możemy przeprowadzić importując procedurę typu modify
i następnie aplikując ją korzystając z funkcji analyzer.modify_wave
. Funkcja ta przyjmuje sigman.Wave
do modyfikacji, początek i koniec zakresu czasowego na jakim należy przeprowadzić procedurę, zaimportowany moduł procedury oraz dict
argumentów oparty na procedure.default_arguments
. Zwróci ona natomiast nowy sigman.Wave
o długości wymaganego zakresu czasu który możemy wykorzystać zamieniając ten sam zakres starego sigman.Wave
metodą Wave.replace_slice
.
Przykład filtrowania wycinka przebiegu EKG.
import sigman as sm
from sigman import file_manager as fm
from sigman import analyzer
from sigman import visualizer as vis
ecg = fm.import_wave('example_data/EKG.dat', 'ecg')
composite_data = sm.Composite_data(waves={'ecg':ecg})
butterworth = analyzer.import_procedure('modify_filter_butterworth')
arguments = butterworth.default_arguments
arguments['N'] = 3
arguments['Wn'] = 30
filtered_wave = analyzer.modify_wave(composite_data.waves['ecg'], 55, 60, butterworth, arguments)
composite_data.waves['ecg'].replace_slice(55,60, filtered_wave)
vis.visualize_composite_data(composite_data, begin_time=55, end_time=65,
title="Porównanie przefiltrowanego fragmentu <55s, 60s> i nieprzefiltrowanego <60s, 65s>")
Punkty możemy odnaleźć importując procedurę typu points
i aplikując ją funkcją analyzer.find_points
. Funkcja ta przymuje jako argumenty sigman.Composite_data
, który chcemy badać, początek oraz koniec zakresu czasowego, moduł procedury oraz argumenty oparte na procedure.default_arguments
. Zwróci ona sigman.Points
, który możemy dodać do reszty danych przez Composite_data.add_points
lub do innego istniejącego zestawu punktów przez Points.add_points
.
Przykład odnajdywania punktów DBP na całym przebiegu BP.
import sigman as sm
from sigman import file_manager as fm
from sigman import analyzer
from sigman import visualizer as vis
bp = fm.import_wave('example_data/BP.dat', 'bp')
composite_data = sm.Composite_data(waves={'bp':bp})
dbp_finder = analyzer.import_procedure('points_dbp_simple')
# obliczamy zakres na którym mamy wszystkie dane (przebieg BP)
begin_time, end_time = composite_data.calculate_time_range(['bp'])
dbp = analyzer.find_points(composite_data, begin_time, end_time, dbp_finder, dbp_finder.default_arguments)
composite_data.add_points(dbp, 'dbp')
vis.visualize_composite_data(composite_data)
Parametry możemy obliczyć importując procedurę typu parameter
i aplikując ją funkcją analyzer.calculate_parameter
. Funkcja ta przyjmuje sigman.Composite_data
, listę tuples zawierających początek i koniec zakresów czasowych na których chcemy parametr obliczyć, moduł procedury i dodatkowe argumenty oparte na procedure.default_arguments
.
Przykład obliczenia częstotliwości bicia serca na zakresach <0s,15s>, <15s,60s> oraz <60s,120s> w oparciu o przebieg EKG.
import sigman as sm
from sigman import file_manager as fm
from sigman import analyzer
from sigman import visualizer as vis
ecg = fm.import_wave('example_data/EKG.dat', 'ecg')
composite_data = sm.Composite_data(waves={'ecg':ecg})
# odnajdujemy punkty R
r_finder = analyzer.import_procedure('points_r_simple')
begin_time, end_time = composite_data.calculate_time_range(['ecg'])
r = analyzer.find_points(composite_data, begin_time, end_time, r_finder, r_finder.default_arguments)
composite_data.add_points(r, 'r')
# obliczamy HR
hr_proc = analyzer.import_procedure('parameter_heart_rate')
param_tuples = [(0,15),(15,60),(60,120)]
hr = analyzer.calculate_parameter(composite_data, param_tuples, hr_proc, hr_proc.default_arguments)
composite_data.add_parameter(hr, 'hr')
vis.visualize_composite_data(composite_data) # Jak na razie wizualizacja parametrów jest niedopracowana
Przykłady użycia różnych funkcji biblioteki w pliku test_sigman.py
.
Biblioteka nadaje się do wstępnego użytku. Obecnie ważne jest napisanie większej liczby procedur.
- Wczytywanie danych
- z plików
.dat
- z signalyzera
- z plików
- Zapisywanie danych
- sygnałów / punktów w plikach
.dat
- całych projektów / niedokończonej pracy
- obliczonych parametrów w
.xslx
/.csv
- sygnałów / punktów w plikach
- Operowanie na danych
- obróbkę danych na odcinkach czasowych zewnętrznymi procedurami
- filtracja / modyfikacja sygnału
- wykrywanie punktów
- obliczanie średnich parametrów
- usuwanie i dodawanie pojedynczych punktów
- przesuwanie przebiegów i punktów względem siebie w czasie
- obróbkę danych na odcinkach czasowych zewnętrznymi procedurami
- Wizualizacja danych
- prosty mechanizm wizualizacji przebiegów, punktów i parametrów
Aplikacja wymaga pracy nim będzie w stanie użytkowym.
- Główne okno
- wykres danych
- przebiegi
- punktowy
- parametry do uzgodnienia
- interaktywność
- skalowanie
- przesuwanie
- ukrywanie i pokazywanie elementów
- sprawdzanie dokładnych wartości
- manualna edycja punktów
- listy obiektów
- przebiegi
- punkty
- parametry
- interaktywność
- edycja metainformacji
- tryb edycji punktów
- wyświetlanie metainformacji o danych na listach
- wykres danych
- Obsługa plików
- importowanie przebiegów i punktów
- eksportowanie przebiegów i punktów
- zapisywanie i wczytywanie projektu
- Obsługa procedur
- modyfikacja przebiegów
- odnajdywanie punktów
- obliczanie parametrów
- przeprowadzanie całościowej analizy (kilka procedur na raz)
- wizualizacja wyniku działania procedury przed potwierdzeniem
Kod projektu powinien być utrzymywany jak najbliżej norm PEP-8. W bibliotece sigman utrzymywane jest nazewnictwo lowercase_with_underscores
, natomiast w kodzie aplikacji QtSigman w ramach konsystencji z biblioteką PyQt5 wykrozystywane jest nazewnictwo CamelCase
.
Obecnie dokumentacja jest po polsku i trwają prace nad przetłumaczeniem jej na angielski. Dokumentacja nowo powstałego kodu powinna już być tylko po angielsku.
- Instytut Medycyny Doświadczalnej i Klinicznej im. M. Mossakowskiego PAN - wsparcie i nakierowanie projektu, dane