suvtools 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
suvtools-1.0.0/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-Present Pranab Das (GitHub: @pranabdas)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ of the Software, and to permit persons to whom the Software is furnished to do
10
+ so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.1
2
+ Name: suvtools
3
+ Version: 1.0.0
4
+ Summary: Python library for analyzing and visualizing SSLS SUV Beamline data.
5
+ Home-page: https://pranabdas.github.io/suvtools/
6
+ License: MIT
7
+ Keywords: python,data-analysis,matplotlib,materials-science,surface-science,condensed-matter-physics,photoelectron-spectroscopy,arpes
8
+ Author: Pranab Das
9
+ Requires-Python: >=3.9,<4.0
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Dist: matplotlib (>=3.9.2,<4.0.0)
17
+ Requires-Dist: numpy (>=2.0.2,<3.0.0)
18
+ Requires-Dist: pandas (>=2.2.3,<3.0.0)
19
+ Requires-Dist: scipy (>=1.13.1,<2.0.0)
20
+ Project-URL: Documentation, https://pranabdas.github.io/suvtools/docs/
21
+ Project-URL: Repository, https://github.com/pranabdas/suvtools
22
+ Description-Content-Type: text/markdown
23
+
24
+ <h1 align="center">SUV Tools</h1>
25
+
26
+ <p align="center">
27
+ <a href="https://github.com/pranabdas/suvtools/actions/workflows/python-tests.yml"><img src="https://github.com/pranabdas/suvtools/actions/workflows/python-tests.yml/badge.svg" alt="Python tests status"></a>
28
+ <a href="https://github.com/pranabdas/suvtools/actions/workflows/deploy-gh-pages.yml"><img src="https://github.com/pranabdas/suvtools/actions/workflows/deploy-gh-pages.yml/badge.svg" alt="Deploy gh-pages status"></a>
29
+ <a href="https://github.com/pranabdas/suvtools/blob/master/LICENSE"><img src="https://img.shields.io/github/license/sourcerer-io/hall-of-fame.svg?colorB=A31F34"></a>
30
+ <a href="https://github.com/pranabdas/suvtools/releases/latest"><img src="https://img.shields.io/github/v/release/pranabdas/suvtools.svg" alt="Latest Release"/></a>
31
+ </p>
32
+
33
+
34
+ Visit the project homepage <https://pranabdas.github.io/suvtools/>
35
+
36
+ ## Quick start
37
+
38
+ Install latest stable release:
39
+ ```console
40
+ pip3 install --upgrade suvtools
41
+ ```
42
+
43
+ Import suvtools into your project:
44
+ ```python
45
+ import suvtools as suv
46
+ ```
47
+
48
+ **Modules:**
49
+
50
+ - `suv.load("datafile.txt", scan=None)`: It will return a two dimensional array
51
+ with columns for various parameters. If the second argument, i.e., the scan
52
+ number is not specified, the code will read the last scan from the file.
53
+
54
+ - `suv.fit_gauss(x, y, a=None, x0=None, sigma=None, xmin=None, xmax=None, num=1000)`:
55
+ returns x, Gaussian fitted y values, and prints out relevant parameters. `xmin`
56
+ and `xmax` determines the range to fit. If `xmin` and `xmax` are not provided,
57
+ whole range is used. `num` determines the number of points returned in `x_fit`
58
+ and `y_fit`.
59
+
60
+ - `suv.fit_lorentz(x, y, a=None, x0=None, gamma=None, xmin=None, xmax=None, num=1000)`:
61
+ returns x, Lorentzian fitted y values, and prints out relevant parameters.
62
+ `xmin` and `xmax` determines the range to fit. If `xmin` and `xmax` are not
63
+ provided, whole range is used. `num` determines the number of points returned in
64
+ `x_fit` and `y_fit`.
65
+
66
+ - `suv.save_csv("datafile.txt", csvname=None, scan=None)`: saves scan to a csv
67
+ file. The file will be saved in the save directory as `datafile` with name
68
+ `datafile.csv` unless `csvname` is specified. Like the `load` module, if the
69
+ scan number is not specified, it will read the last scan from the file.
70
+
71
+ - `suv.norm_bg(energy, intensity, x1, x2, x_norm_loc=None)`:
72
+ Removes linear background, and normalizes the data. x1, x2 are energy values
73
+ that determines the slope of the background. By default the normalization done
74
+ at the tail point of the spectra. It can be changed to other point, enter the
75
+ corresponding energy value. The intention is to normalize at an energy value
76
+ away from the peaks/features of interest.
77
+
78
+ - `suv.lock_peak(data, refdata, x1=None, x2=None, E_col=0, I_col=9, I0_col=4)`:
79
+ Locks peak position with respect to the reference data. It locks the maximum of
80
+ intensity to the same energy; the range of peak search can be specified by input
81
+ `x1` and `x2`. If no bounds are given, it will find the maximum in the whole
82
+ data range.
83
+
84
+ - `suv.calc_area(y, x, x_start=None, x_end=None)`:
85
+ Calculates area under the curve for given `x` and `y` values. `x_start` and
86
+ `x_end` can be specified to set the limit of integration region, if not provided
87
+ whole range is integrated.
88
+
89
+ See the notebook for some example usage.
90
+
91
+ ### Documentation development
92
+
93
+ ```console
94
+ # install npm packages
95
+ npm install
96
+
97
+ # serve locally
98
+ npm start
99
+
100
+ # build
101
+ npm run build
102
+
103
+ # deploy to github
104
+ npm run gh-deploy
105
+ ```
106
+
107
+ ### Python tests
108
+
109
+ ```console
110
+ python3 -m unittest discover tests
111
+ ```
112
+
@@ -0,0 +1,88 @@
1
+ <h1 align="center">SUV Tools</h1>
2
+
3
+ <p align="center">
4
+ <a href="https://github.com/pranabdas/suvtools/actions/workflows/python-tests.yml"><img src="https://github.com/pranabdas/suvtools/actions/workflows/python-tests.yml/badge.svg" alt="Python tests status"></a>
5
+ <a href="https://github.com/pranabdas/suvtools/actions/workflows/deploy-gh-pages.yml"><img src="https://github.com/pranabdas/suvtools/actions/workflows/deploy-gh-pages.yml/badge.svg" alt="Deploy gh-pages status"></a>
6
+ <a href="https://github.com/pranabdas/suvtools/blob/master/LICENSE"><img src="https://img.shields.io/github/license/sourcerer-io/hall-of-fame.svg?colorB=A31F34"></a>
7
+ <a href="https://github.com/pranabdas/suvtools/releases/latest"><img src="https://img.shields.io/github/v/release/pranabdas/suvtools.svg" alt="Latest Release"/></a>
8
+ </p>
9
+
10
+
11
+ Visit the project homepage <https://pranabdas.github.io/suvtools/>
12
+
13
+ ## Quick start
14
+
15
+ Install latest stable release:
16
+ ```console
17
+ pip3 install --upgrade suvtools
18
+ ```
19
+
20
+ Import suvtools into your project:
21
+ ```python
22
+ import suvtools as suv
23
+ ```
24
+
25
+ **Modules:**
26
+
27
+ - `suv.load("datafile.txt", scan=None)`: It will return a two dimensional array
28
+ with columns for various parameters. If the second argument, i.e., the scan
29
+ number is not specified, the code will read the last scan from the file.
30
+
31
+ - `suv.fit_gauss(x, y, a=None, x0=None, sigma=None, xmin=None, xmax=None, num=1000)`:
32
+ returns x, Gaussian fitted y values, and prints out relevant parameters. `xmin`
33
+ and `xmax` determines the range to fit. If `xmin` and `xmax` are not provided,
34
+ whole range is used. `num` determines the number of points returned in `x_fit`
35
+ and `y_fit`.
36
+
37
+ - `suv.fit_lorentz(x, y, a=None, x0=None, gamma=None, xmin=None, xmax=None, num=1000)`:
38
+ returns x, Lorentzian fitted y values, and prints out relevant parameters.
39
+ `xmin` and `xmax` determines the range to fit. If `xmin` and `xmax` are not
40
+ provided, whole range is used. `num` determines the number of points returned in
41
+ `x_fit` and `y_fit`.
42
+
43
+ - `suv.save_csv("datafile.txt", csvname=None, scan=None)`: saves scan to a csv
44
+ file. The file will be saved in the save directory as `datafile` with name
45
+ `datafile.csv` unless `csvname` is specified. Like the `load` module, if the
46
+ scan number is not specified, it will read the last scan from the file.
47
+
48
+ - `suv.norm_bg(energy, intensity, x1, x2, x_norm_loc=None)`:
49
+ Removes linear background, and normalizes the data. x1, x2 are energy values
50
+ that determines the slope of the background. By default the normalization done
51
+ at the tail point of the spectra. It can be changed to other point, enter the
52
+ corresponding energy value. The intention is to normalize at an energy value
53
+ away from the peaks/features of interest.
54
+
55
+ - `suv.lock_peak(data, refdata, x1=None, x2=None, E_col=0, I_col=9, I0_col=4)`:
56
+ Locks peak position with respect to the reference data. It locks the maximum of
57
+ intensity to the same energy; the range of peak search can be specified by input
58
+ `x1` and `x2`. If no bounds are given, it will find the maximum in the whole
59
+ data range.
60
+
61
+ - `suv.calc_area(y, x, x_start=None, x_end=None)`:
62
+ Calculates area under the curve for given `x` and `y` values. `x_start` and
63
+ `x_end` can be specified to set the limit of integration region, if not provided
64
+ whole range is integrated.
65
+
66
+ See the notebook for some example usage.
67
+
68
+ ### Documentation development
69
+
70
+ ```console
71
+ # install npm packages
72
+ npm install
73
+
74
+ # serve locally
75
+ npm start
76
+
77
+ # build
78
+ npm run build
79
+
80
+ # deploy to github
81
+ npm run gh-deploy
82
+ ```
83
+
84
+ ### Python tests
85
+
86
+ ```console
87
+ python3 -m unittest discover tests
88
+ ```
@@ -0,0 +1,32 @@
1
+ [tool.poetry]
2
+ name = "suvtools"
3
+ version = "1.0.0"
4
+ description = "Python library for analyzing and visualizing SSLS SUV Beamline data."
5
+ authors = ["Pranab Das"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ homepage = "https://pranabdas.github.io/suvtools/"
9
+ repository = "https://github.com/pranabdas/suvtools"
10
+ documentation = "https://pranabdas.github.io/suvtools/docs/"
11
+ keywords = [
12
+ "python", "data-analysis", "matplotlib", "materials-science",
13
+ "surface-science", "condensed-matter-physics", "photoelectron-spectroscopy",
14
+ "arpes"]
15
+
16
+ [tool.poetry.dependencies]
17
+ python = "^3.9"
18
+ matplotlib = "^3.9.2"
19
+ numpy = "^2.0.2"
20
+ pandas = "^2.2.3"
21
+ scipy = "^1.13.1"
22
+
23
+ [tool.poetry.group.dev.dependencies]
24
+ autopep8 = "^2.3.1"
25
+ jupyterlab = "^4.2.5"
26
+
27
+ [tool.poetry.group.tests.dependencies]
28
+ psutil = "^6.0.0"
29
+
30
+ [build-system]
31
+ requires = ["poetry-core"]
32
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: SUV tools
5
+ Version: 20220214
6
+ @author: Pranab Das (GitHub: @pranabdas)
7
+ """
8
+ from .load import load
9
+ from .fit_gauss import fit_gauss
10
+ from .fit_lorentz import fit_lorentz
11
+ from .save_csv import save_csv
12
+ from .norm_bg import norm_bg
13
+ from .lock_peak import lock_peak
14
+ from .calc_area import calc_area
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Calculate area under the curve
5
+ Version: 20220214
6
+ @author: Pranab Das (GitHub: @pranabdas)
7
+ area = suv.calc_area(y, x, x_end=None, x_end=None)
8
+ """
9
+
10
+
11
+ def calc_area(y, x, x_start=None, x_end=None):
12
+ import numpy as np
13
+
14
+ if x_start:
15
+ start_index = np.argmin(abs(x - x_start))
16
+ else:
17
+ start_index = 0
18
+
19
+ if x_end:
20
+ end_index = np.argmin(abs(x - x_end))
21
+ else:
22
+ end_index = -1
23
+
24
+ return np.trapezoid(y[start_index:end_index], x[start_index:end_index])
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Fit peaks with Gaussian distribution
5
+ Version: 20220214
6
+ @author: Pranab Das (GitHub: @pranabdas)
7
+ data = suv.fit_gauss(x, y, a=None, x0=None, sigma=None, xmin=None, xmax=None)
8
+ """
9
+
10
+
11
+ def fit_gauss(x, y, a=None, x0=None, sigma=None, xmin=None, xmax=None, num=1000):
12
+ import numpy as np
13
+ from scipy import optimize
14
+
15
+ def gauss(x, a, x0, sigma):
16
+ return a * np.exp(-((x - x0) ** 2) / (2 * sigma**2))
17
+
18
+ if xmin:
19
+ xmin = float(xmin)
20
+ xmin_id = np.argmin(abs(x - xmin))
21
+ x = x[xmin_id:]
22
+ y = y[xmin_id:]
23
+ else:
24
+ xmin = x[0]
25
+
26
+ if xmax:
27
+ xmax = float(xmax)
28
+ xmax_id = np.argmin(abs(x - xmax))
29
+ x = x[:xmax_id]
30
+ y = y[:xmax_id]
31
+ else:
32
+ xmax = x[-1]
33
+
34
+ if not a:
35
+ a = max(y)
36
+
37
+ if not x0:
38
+ x0 = x[np.argmax(y)]
39
+
40
+ if not sigma:
41
+ sigma = abs(x[-1] - x[0]) / 10
42
+
43
+ params, _ = optimize.curve_fit(gauss, x, y, p0=[a, x0, sigma])
44
+
45
+ print(
46
+ "\n#---- Gaussian fitting parameters ----#\na =",
47
+ params[0],
48
+ "\nx0 =",
49
+ params[1],
50
+ "\nsigma =",
51
+ params[2],
52
+ "\nFWHM =",
53
+ 2.355 * params[2],
54
+ "\n#----------------------------------------#",
55
+ )
56
+
57
+ fit_x = np.linspace(xmin, xmax, num)
58
+ fit_y = gauss(fit_x, params[0], params[1], params[2])
59
+
60
+ return fit_x, fit_y
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Fit peaks with Lorentzian distribution
5
+ Version: 20220214
6
+ @author: Pranab Das (GitHub: @pranabdas)
7
+ data = suv.fit_lorentz(x, y, a=None, x0=None, gamma=None, xmin=None, xmax=None)
8
+ """
9
+
10
+
11
+ def fit_lorentz(x, y, a=None, x0=None, gamma=None, xmin=None, xmax=None, num=1000):
12
+ import numpy as np
13
+ from scipy import optimize
14
+
15
+ def lorentz(x, a, x0, gamma):
16
+ return a * gamma**2 / (np.pi * gamma * ((x - x0) ** 2 + gamma**2))
17
+
18
+ if xmin:
19
+ xmin = float(xmin)
20
+ xmin_id = np.argmin(abs(x - xmin))
21
+ x = x[xmin_id:]
22
+ y = y[xmin_id:]
23
+ else:
24
+ xmin = x[0]
25
+
26
+ if xmax:
27
+ xmax = float(xmax)
28
+ xmax_id = np.argmin(abs(x - xmax))
29
+ x = x[:xmax_id]
30
+ y = y[:xmax_id]
31
+ else:
32
+ xmax = x[-1]
33
+
34
+ if not a:
35
+ a = max(y)
36
+
37
+ if not x0:
38
+ x0 = x[np.argmax(y)]
39
+
40
+ if not gamma:
41
+ gamma = abs(x[-1] - x[0]) / 10
42
+
43
+ params, _ = optimize.curve_fit(lorentz, x, y, p0=[a, x0, gamma])
44
+
45
+ print(
46
+ "\n#---- Lorentzian fitting parameters ----#\na =",
47
+ params[0],
48
+ "\nx0 =",
49
+ params[1],
50
+ "\ngamma =",
51
+ params[2],
52
+ "\nFWHM =",
53
+ 2 * params[2],
54
+ "\n#---------------------------------------#",
55
+ )
56
+
57
+ fit_x = np.linspace(xmin, xmax, num)
58
+ fit_y = lorentz(fit_x, params[0], params[1], params[2])
59
+
60
+ return fit_x, fit_y
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Load SUV data
5
+ Version: 20220214
6
+ @author: Pranab Das (GitHub: @pranabdas)
7
+ data = suv.load("data.txt", 12)
8
+ """
9
+
10
+
11
+ def load(filename, scan=None):
12
+ import numpy as np
13
+ import urllib.request
14
+ import itertools
15
+
16
+ if (filename[:7] == "http://") or (filename[:8] == "https://"):
17
+ web = True
18
+ else:
19
+ web = False
20
+
21
+ if web:
22
+ try:
23
+ headers = {"User-Agent": "Mozilla/5.0"}
24
+ req = urllib.request.Request(url=filename, headers=headers)
25
+ contents = urllib.request.urlopen(req).read().decode()
26
+ contents = contents.splitlines()
27
+ except:
28
+ print("Could not read url.")
29
+
30
+ else:
31
+ fid = open(filename, "r")
32
+ if fid.mode == "r":
33
+ contents = fid.read()
34
+ fid.close()
35
+ contents = contents.splitlines()
36
+
37
+ # remove empty lines
38
+ # contents = list(filter(None, contents))
39
+
40
+ # find scan id and corresponding line numbers
41
+ scan_id = []
42
+ line_id = []
43
+
44
+ for line in range(len(contents)):
45
+ line_content = contents[line].split(" ")
46
+ if line_content[0] == "#S":
47
+ scan_id.append(int(line_content[1]))
48
+ line_id.append(line)
49
+
50
+ scan_id = np.array(scan_id)
51
+ # Determine the lines to read for specific scan
52
+ # Default scan set to the last scan
53
+ if not scan:
54
+ scan = scan_id[-1]
55
+
56
+ # In case given scan does not exist in the file
57
+ if scan not in scan_id:
58
+ print("Error: scan not found!")
59
+ return
60
+ else:
61
+ line_start = line_id[np.where(scan_id == scan)[0][0]]
62
+
63
+ if scan == scan_id[-1]:
64
+ line_end = len(contents)
65
+ else:
66
+ line_end = line_id[np.where(scan_id == scan)[0][0] + 1] - 1
67
+
68
+ # Extract the column names
69
+ # for line in range(line_end-line_start):
70
+ # line_content = contents[line+line_start].split(' ')
71
+ # if line_content[0]=='#L':
72
+ # colnames=line_content
73
+ # colnames.pop(0)
74
+ # colnames = list(filter(None, colnames))
75
+ # break
76
+
77
+ # print("Scan #", scan, "loaded. Data columns are: \n", colnames)
78
+
79
+ lines_to_read = itertools.islice(open(filename), line_start, line_end)
80
+ filtered_lines = itertools.dropwhile(lambda x: x.strip()[0] == "#", lines_to_read)
81
+
82
+ return np.loadtxt(filtered_lines)
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Lock the peaks at same energy location
5
+ Version: 20220214
6
+ @author: Pranab Das (GitHub: @pranabdas)
7
+ data = suv.lock_peak(data, refdata, x1=None, x2=None, E_col=0, I_col=9, I0_col=4)
8
+ """
9
+
10
+
11
+ def lock_peak(data, refdata, x1=None, x2=None, E_col=0, I_col=9, I0_col=4):
12
+ import numpy as np
13
+
14
+ if data.shape != refdata.shape:
15
+ print("Error! Data and reference data must have same dimensions!")
16
+ return
17
+
18
+ if not x1:
19
+ x1 = refdata[0, E_col]
20
+ if not x2:
21
+ x2 = refdata[-1, E_col]
22
+
23
+ x1_id = np.argmin(abs(refdata[:, E_col] - x1))
24
+ x2_id = np.argmin(abs(refdata[:, E_col] - x2))
25
+
26
+ ref_peak_id = np.argmax(refdata[x1_id:x2_id, I_col] / refdata[x1_id:x2_id, I0_col])
27
+
28
+ peak_id = np.argmax(data[x1_id:x2_id, I_col] / data[x1_id:x2_id, I0_col])
29
+
30
+ diff_id = peak_id - ref_peak_id
31
+
32
+ if diff_id > 0:
33
+ for _ in range(diff_id):
34
+ data = np.delete(data, 0, 0)
35
+ data = np.vstack((data, data[-1, :]))
36
+ elif diff_id < 0:
37
+ for _ in range(abs(diff_id)):
38
+ data = np.delete(data, -1, 0)
39
+ data = np.vstack((data[0, :], data))
40
+
41
+ data[:, E_col] = refdata[:, E_col]
42
+
43
+ return data
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Subtracts linear background and normalizes the data
5
+ Version: 20240619
6
+ @author: Pranab Das (GitHub: @pranabdas)
7
+ normalized_intensity = suv.norm_bg(energy, intensity, x1, x2, x_norm_loc=None)
8
+ """
9
+
10
+
11
+ def norm_bg(energy, intensity, x1, x2, x_norm_loc=None):
12
+ import numpy as np
13
+
14
+ y1 = intensity[np.argmin(abs(energy - x1))]
15
+ y2 = intensity[np.argmin(abs(energy - x2))]
16
+
17
+ if x_norm_loc:
18
+ x_norm_loc = np.argmin(abs(energy - x_norm_loc))
19
+ else:
20
+ x_norm_loc = -1
21
+
22
+ m = (y2 - y1) / (x2 - x1) # slope
23
+ c = y1 - (m * x1) # intersection
24
+
25
+ bg = (m * energy) + c
26
+ intensity -= bg
27
+ return intensity / intensity[x_norm_loc]
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Export SUV data in CSV format
5
+ Version: 20220214
6
+ @author: Pranab Das (GitHub: @pranabdas)
7
+ data = suv.save_csv(filename, csvname=None, scan=None)
8
+ """
9
+
10
+
11
+ def save_csv(filename, csvname=None, scan=None):
12
+ import numpy as np
13
+ import pandas as pd
14
+ import os
15
+ import warnings
16
+
17
+ fid = open(filename, "r")
18
+ contents = fid.read()
19
+ fid.close()
20
+ contents = contents.splitlines()
21
+
22
+ # find scan id and corresponding line numbers
23
+ scan_id = []
24
+ line_id = []
25
+
26
+ for line in range(len(contents)):
27
+ line_content = contents[line].split(" ")
28
+ if line_content[0] == "#S":
29
+ scan_id.append(int(line_content[1]))
30
+ line_id.append(line)
31
+
32
+ # determine the lines to read for specific scan
33
+ # default scan set to the last scan
34
+ if not scan:
35
+ scan = scan_id[-1]
36
+
37
+ # if given scan does not exist in the file
38
+ if scan not in scan_id:
39
+ print("Error! Scan not found!")
40
+ return
41
+ else:
42
+ line_start = line_id[scan - 1]
43
+ if scan == scan_id[-1]:
44
+ line_end = len(contents)
45
+ else:
46
+ line_end = line_id[scan] - 1
47
+
48
+ if not csvname:
49
+ csvname = filename + "_scan_" + str(scan) + ".csv"
50
+
51
+ if os.path.isfile(csvname):
52
+ if csvname[-4:] == ".csv":
53
+ csvname = csvname[:-4]
54
+ csvname = csvname + "_copy.csv"
55
+
56
+ # Extract the column names
57
+ for line in range(line_end - line_start):
58
+ line_content = contents[line + line_start].split(" ")
59
+ if line_content[0] == "#L":
60
+ colnames = line_content
61
+ colnames.pop(0)
62
+ colnames = list(filter(None, colnames))
63
+
64
+ # suppress numpy warning related to max_rows behavior
65
+ with warnings.catch_warnings():
66
+ warnings.filterwarnings(action="ignore")
67
+ data = np.loadtxt(
68
+ filename,
69
+ comments="#",
70
+ skiprows=(line_start - 1),
71
+ max_rows=(line_end - line_start),
72
+ )
73
+
74
+ # convert to dataframe
75
+ data = pd.DataFrame(data, columns=colnames)
76
+ data.to_csv(csvname, index=False)
77
+ print("Saved data to file:", csvname)