diff --git a/tutorials/0-introduction/data_containers.ipynb b/tutorials/0-introduction/data_containers_history.ipynb similarity index 66% rename from tutorials/0-introduction/data_containers.ipynb rename to tutorials/0-introduction/data_containers_history.ipynb index b6023a9..9b02443 100644 --- a/tutorials/0-introduction/data_containers.ipynb +++ b/tutorials/0-introduction/data_containers_history.ipynb @@ -7,6 +7,40 @@ "# PyKOALA Data Containers" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Table of contents:\n", + "\n", + "1. [Importing class](#importing-class)\n", + "2. [`HistoryRecord` class](#historyrecord-class)\n", + " - [`HistoryRecord` methods](#historyrecord-methods)\n", + " - [`to_str`](#to_str)\n", + "3. [`DataContainerHistory` class](#datacontainerhistory-class)\n", + " - [`DataContainerHistory` methods](#datacontainerhistory-methods)\n", + " - [`initialise_record`](#initialise_record)\n", + " - [`log_record`](#log_record)\n", + " - [`is_record`](#is_record)\n", + " - [`find_record`](#find_record)\n", + " - [`dump_to_header`](#dump_to_header)\n", + " - [`dump_to_text`](#dump_to_text)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Importing class" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note: Make sure to run the following cells in order to ensure correct execution.**" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -16,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -46,9 +80,18 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Title of history record: Error log\n", + "Comments in history record: ['This is line one.', 'This is line two.']\n" + ] + } + ], "source": [ "from pykoala.data_container import HistoryRecord\n", "\n", @@ -57,6 +100,20 @@ "print('Comments in history record: ', hist_record.comments)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `HistoryRecord` **methods** " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `to_str`" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -66,9 +123,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Error log: This is line one.\\nThis is line two.'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "hist_record.to_str()" ] @@ -82,9 +150,18 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is line one.\n", + "This is line two.\n" + ] + } + ], "source": [ "for record in hist_record.comments:\n", " print(record)" @@ -106,9 +183,19 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Step 1: Loaded data\n", + "Step 2: Filtered outliers\n", + "Step 3: Normalized data\n" + ] + } + ], "source": [ "from pykoala.data_container import DataContainerHistory\n", "initial_entries = [\n", @@ -128,14 +215,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Functions " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Note:** Make sure to run the following cells in order to ensure correct execution." + "### `DataContainerHistory` **methods** " ] }, { @@ -154,9 +234,19 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Step 1: Loaded data\n", + "Step 2: Filtered outliers\n", + "Step 3: Normalized data\n" + ] + } + ], "source": [ "initial_entries = [\n", " (\"Step 1\", \"Loaded data\"),\n", @@ -193,9 +283,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Step 1: Loaded data\n", + "Step 2: Filtered outliers\n", + "Step 3: Normalized data\n", + "Step 4: Additional data added with log_record method\n" + ] + } + ], "source": [ "#We use the same history log as before\n", "initial_entries = [\n", @@ -228,9 +329,18 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n" + ] + } + ], "source": [ "print(history_log.is_record(title='Step 1'))\n", "print(history_log.is_record(title='Step 42'))" @@ -259,9 +369,18 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of found items: 1\n", + "Step 1: Loaded data\n" + ] + } + ], "source": [ "search_results = history_log.find_record(title='Step 1')\n", "print('Number of found items:', len(search_results))\n", @@ -279,9 +398,19 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of found items: 2\n", + "Step 1: Loaded data\n", + "Step 1: Rinse and repeat\n" + ] + } + ], "source": [ "history_log_repeated_titles = DataContainerHistory()\n", "history_log_repeated_titles.initialise_record(list_of_entries=initial_entries)\n", @@ -311,9 +440,23 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PYKOALA0= 'Loaded data' / Step 1 \n", + "PYKOALA1= 'Filtered outliers' / Step 2 \n", + "PYKOALA2= 'Normalized data' / Step 3 \n", + "PYKOALA3= 'Additional data added with log_record method' / Step 4 " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "from astropy.io import fits\n", "history_log.dump_to_header()\n" @@ -335,16 +478,25 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/mbolivar/pykoala-tutorials/tutorials/0-introduction\n", + "[pykoala] 2024/12/11 10:07|INFO> Writting log into text file\n" + ] + } + ], "source": [ "from pathlib import Path\n", "import os\n", "\n", "os.system('pwd')\n", - "path_to_file = './output/history_log.txt'\n", - "history_log.dump_to_text(file=path_to_file)" + "path_output = './output/history_log.txt'\n", + "history_log.dump_to_text(file=path_output)" ] }, { @@ -375,5 +527,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/tutorials/0-introduction/data_containers_rss.ipynb b/tutorials/0-introduction/data_containers_rss.ipynb new file mode 100644 index 0000000..9ea82d3 --- /dev/null +++ b/tutorials/0-introduction/data_containers_rss.ipynb @@ -0,0 +1,636 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# PyKOALA Raw Stacked Spectra (RSS) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Table of contents:\n", + "\n", + "1. [Importing class](#importing-class)\n", + "2. [Koala RSS](#koala-rss)\n", + " - [`RSS` Attributes](#rss-attributes)\n", + " - [General information](#general-information)\n", + " - [History record](#history-record)\n", + " - [Data methods](#data-methods)\n", + " - [`is_corrected`](#is_corrected)\n", + " - [`get_centre_of_mass`](#get_centre_of_mass)\n", + " - [`update_coordinates`](#update_coordinates)\n", + " - [`get_integrated_fibres`](#get_integrated_fibres)\n", + " - [`to_fits`](#to_fits)\n", + " - [`from_fits`](#from_fits)\n", + " - [Plotting methods](#plotting-methods)\n", + " - [`plot_rss_image`](#plot_rss_image)\n", + " - [`plot_mask`](#plot_mask)\n", + " - [`plot_fibres`](#plot_fibres)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Importing class" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note: Make sure to run the following cells in order to ensure correct execution.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pykoala.data_container import RSS\n", + "import os\n", + "from pykoala.instruments.koala_ifu import koala_rss\n", + "from astropy.io import fits\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Koala RSS" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can extract the RSS data using the function `koala_rss` in `koala_ifu`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data_path = '../data/koala/'\n", + "single_fits_sample = '385R/27feb20028red.fits'\n", + "file_sample = os.path.join(data_path, single_fits_sample)\n", + "rss_sample = koala_rss(file_sample)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### RSS attributes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### General information " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Information about the RRS can be obtained from the `info` attribute:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.info" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The information is given as a dictionary:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.info.keys()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Intensity values can be shown with: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.intensity" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Intensity is saved as a `astropy` quantity, so we can extract its unit as an attribute:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "units_intensity = rss_sample.intensity.unit\n", + "print(f'Unit of the intensity data is: {units_intensity}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similarly, with the variance of the RSS:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "units_variance = rss_sample.variance.unit\n", + "\n", + "print(f'Variance: {rss_sample.variance}')\n", + "print('\\n================================\\n')\n", + "print(f'Unit of the intensity data is: {units_variance}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### History record" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "RSS metada is stored as a `DataContainerHistory` object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.history.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### RSS fibres" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Information about the fibres can be obtained from here:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.fibre_diameter" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With `science_fibres` we can find the indexes of the fibres with non-bad pixels:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "number_of_non_bad_fibres = len(rss_sample.science_fibres)\n", + "print(f'Indexes of non-bad fibers: {rss_sample.science_fibres}')\n", + "print('\\n================================\\n')\n", + "print(f'Number of non-bad fibers: {number_of_non_bad_fibres}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Data methods" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `is_corrected`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can check if a correction has been done in the RSS data with the `is_corrected` method: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.is_corrected(correction='read')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `get_centre_of_mass`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can show the center of mass (COM) based on the RSS fibre positions:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.get_centre_of_mass()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first and second lists contain ra and dec coordinates, respectively." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `update_coordinates`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can modify the fibres' coordinates with this method. First, let's check the original coordinates:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "original_ra, original_dec = rss_sample.info[\"fib_ra\"], rss_sample.info[\"fib_dec\"]\n", + "print(f'Original RA fibres coordinates: {original_ra[:10]}')\n", + "print('\\n================================\\n')\n", + "print(f'Original DEC fibres coordinates: {original_dec[:10]}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Now we use `update_coordinates`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.update_coordinates(new_coords=(2*original_ra,2*original_dec))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Updated RA fibres coordinates: {rss_sample.info[\"fib_ra\"][:10]}')\n", + "print('\\n================================\\n')\n", + "print(f'Updated DEC fibres coordinates: {rss_sample.info[\"fib_dec\"][:10]}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `get_integrated_fibres`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We compute the integrated intensity of the RSS fibres with this method:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.get_integrated_fibres()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also constraint the calculation in a specific wavelength." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.wavelength" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we select the wavelenght range. **Make sure to use astropy quantities and not simple float values.** \n", + "\n", + "In this case we will use the same elements of the RSS wavelengh data to define the range." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.get_integrated_fibres(wavelength_range=[6000,7000])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `to_fits`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can write the RSS into a FITS file with this method. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "path_output = './output/'\n", + "fits_file_path = path_output + 'rss_sample.fits'\n", + "rss_sample.to_fits(filename=fits_file_path,overwrite=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice that the format of the FITS file is different from the original." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `from_fits` " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This FITS format can be read with this method:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "new_rss_sample = RSS.from_fits(filename=fits_file_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that we call this method from the RSS class, as oposed as from the object like the previous methods shown here.\n", + "\n", + "The new object has the same properties as `rss_sample`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "new_rss_sample.info" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "comparing_intensity_values = np.array_equal(new_rss_sample.intensity,rss_sample.intensity,equal_nan=True)\n", + "comparing_variance_values = np.array_equal(new_rss_sample.variance,rss_sample.variance,equal_nan=True)\n", + "print(f\"Comparing intensity values: {comparing_intensity_values}\")\n", + "print(f\"Comparing variance values: {comparing_variance_values}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Plotting methods" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can directly do RSS related plots with the following methods " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `plot_rss_image`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot of basic RSS properties:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.plot_rss_image()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Custom version:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "plot_file_path = path_output + '/custom_plot_rss_image.png'\n", + "rss_sample.plot_rss_image(data=rss_sample.variance.value, data_label='Variance',fibre_range=(100,300),\n", + " wavelength_range=(6000,7000),output_filename=plot_file_path)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `plot_mask`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This method creates a plot of the bitmask data:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.plot_mask()\n", + "plt.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `plot_fibres`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This methods plots a fibre map image, showing the spatial distribution of data across fibres." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rss_sample.plot_fibres()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv_koala", + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}