pycoustic 0.1.5__tar.gz → 0.1.7__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.
- {pycoustic-0.1.5 → pycoustic-0.1.7}/PKG-INFO +14 -6
- {pycoustic-0.1.5 → pycoustic-0.1.7}/README.md +10 -4
- {pycoustic-0.1.5 → pycoustic-0.1.7}/pycoustic/__init__.py +2 -1
- {pycoustic-0.1.5 → pycoustic-0.1.7}/pycoustic/survey.py +39 -57
- {pycoustic-0.1.5 → pycoustic-0.1.7}/pycoustic/tkgui.py +2 -2
- {pycoustic-0.1.5 → pycoustic-0.1.7}/pycoustic/weather.py +5 -9
- {pycoustic-0.1.5 → pycoustic-0.1.7}/pyproject.toml +4 -2
- {pycoustic-0.1.5 → pycoustic-0.1.7}/pycoustic/log.py +0 -0
@@ -1,17 +1,19 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: pycoustic
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.7
|
4
4
|
Summary:
|
5
5
|
Author: thumpercastle
|
6
6
|
Author-email: tony.ryb@gmail.com
|
7
|
-
Requires-Python: >=3.
|
7
|
+
Requires-Python: >=3.11.9,<4.0
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
9
|
Classifier: Programming Language :: Python :: 3.12
|
10
10
|
Classifier: Programming Language :: Python :: 3.13
|
11
11
|
Requires-Dist: numpy (==2.2.6)
|
12
12
|
Requires-Dist: openpyxl (==3.1.5)
|
13
13
|
Requires-Dist: pandas (==2.2.3)
|
14
|
+
Requires-Dist: plotly (==6.1.2)
|
14
15
|
Requires-Dist: requests (>=2.32.4,<3.0.0)
|
16
|
+
Requires-Dist: streamlit (>=1.46.1,<2.0.0)
|
15
17
|
Description-Content-Type: text/markdown
|
16
18
|
|
17
19
|
# pycoustic - Toolkit for Analysing Noise Survey Data
|
@@ -70,6 +72,7 @@ pip install pycoustic
|
|
70
72
|
```
|
71
73
|
4. **Analyse the Survey Data**
|
72
74
|
The following are methods of the Survey() object representing the typical use cases for acoustic consultants in the UK.
|
75
|
+
|
73
76
|
### Survey.resi_summary()
|
74
77
|
This method provides a summary of the measurement data for residential projects, with a focus on typical assessment procedures in the UK.
|
75
78
|
It presents A-weighted Leqs for each day and night period (and evenings, if enabled), as well as the nth-highest LAmax during each night-time period.
|
@@ -79,21 +82,26 @@ pip install pycoustic
|
|
79
82
|
**lmax_n** *Int* *(default 10)* The nth-highest value to present.\
|
80
83
|
**lmax_t** *Str* *(default "2min")* The time period T over which Lmaxes are presented. This must be equal to or longer than the period of the raw data.
|
81
84
|
|
82
|
-
## Survey.
|
85
|
+
## Survey.modal()
|
83
86
|
Compute the modal L90 for daytime, evening (if enabled) and night-time periods. By default, this is set to T=60min for (23:00 to 07:00) periods, and T=15min for night-time (23:00 to 07:00) periods, as per BS 4142:2014.
|
84
87
|
|
88
|
+
## Survey.counts()
|
89
|
+
Returns the L90 counts for daytime, evening and night-time periods. You can also choose to include other columns.
|
90
|
+
|
85
91
|
## Survey.lmax_spectra()
|
86
92
|
Compute the Lmax Event spectra for the nth-highest Lmax during each night-time period.\
|
87
93
|
**Note** the date presented alongside the Lmax event is actually the starting date of the night-time period. i.e. an Lmax event with a stamp of 20/12/2024 at 01:22 would actually have occurred on 21/12/2024 at 01:22. These stamps can also sometimes be out by a minute (known bug).
|
88
94
|
|
89
|
-
## Survey.
|
95
|
+
## Survey.leq_spectra()
|
90
96
|
Compute the Leq spectra for daytime, evening (if enabled) and night-time periods. This will present the overall Leqs across the survey, not the Leq for each day.
|
91
|
-
|
97
|
+
|
92
98
|
## Survey.weather()
|
93
99
|
Returns a pandas dataframe of the weather history over the course of your survey.
|
94
100
|
Requires an **api_key** argument. This method makes a call to the OpenWeatherMap OneCall API (see https://openweathermap.org/api). You need to sign up and pass your API key as a string to the weather() method.
|
95
101
|
|
96
|
-
|
102
|
+
## Survey.weather_summary()
|
103
|
+
Returns a pandas dataframe summary of the weather history, comprising maximum, minimum and mean values.
|
104
|
+
You must have called Survey.weather() at least once before to get the summary.
|
97
105
|
|
98
106
|
### Other methods
|
99
107
|
### Known issues
|
@@ -54,6 +54,7 @@ pip install pycoustic
|
|
54
54
|
```
|
55
55
|
4. **Analyse the Survey Data**
|
56
56
|
The following are methods of the Survey() object representing the typical use cases for acoustic consultants in the UK.
|
57
|
+
|
57
58
|
### Survey.resi_summary()
|
58
59
|
This method provides a summary of the measurement data for residential projects, with a focus on typical assessment procedures in the UK.
|
59
60
|
It presents A-weighted Leqs for each day and night period (and evenings, if enabled), as well as the nth-highest LAmax during each night-time period.
|
@@ -63,21 +64,26 @@ pip install pycoustic
|
|
63
64
|
**lmax_n** *Int* *(default 10)* The nth-highest value to present.\
|
64
65
|
**lmax_t** *Str* *(default "2min")* The time period T over which Lmaxes are presented. This must be equal to or longer than the period of the raw data.
|
65
66
|
|
66
|
-
## Survey.
|
67
|
+
## Survey.modal()
|
67
68
|
Compute the modal L90 for daytime, evening (if enabled) and night-time periods. By default, this is set to T=60min for (23:00 to 07:00) periods, and T=15min for night-time (23:00 to 07:00) periods, as per BS 4142:2014.
|
68
69
|
|
70
|
+
## Survey.counts()
|
71
|
+
Returns the L90 counts for daytime, evening and night-time periods. You can also choose to include other columns.
|
72
|
+
|
69
73
|
## Survey.lmax_spectra()
|
70
74
|
Compute the Lmax Event spectra for the nth-highest Lmax during each night-time period.\
|
71
75
|
**Note** the date presented alongside the Lmax event is actually the starting date of the night-time period. i.e. an Lmax event with a stamp of 20/12/2024 at 01:22 would actually have occurred on 21/12/2024 at 01:22. These stamps can also sometimes be out by a minute (known bug).
|
72
76
|
|
73
|
-
## Survey.
|
77
|
+
## Survey.leq_spectra()
|
74
78
|
Compute the Leq spectra for daytime, evening (if enabled) and night-time periods. This will present the overall Leqs across the survey, not the Leq for each day.
|
75
|
-
|
79
|
+
|
76
80
|
## Survey.weather()
|
77
81
|
Returns a pandas dataframe of the weather history over the course of your survey.
|
78
82
|
Requires an **api_key** argument. This method makes a call to the OpenWeatherMap OneCall API (see https://openweathermap.org/api). You need to sign up and pass your API key as a string to the weather() method.
|
79
83
|
|
80
|
-
|
84
|
+
## Survey.weather_summary()
|
85
|
+
Returns a pandas dataframe summary of the weather history, comprising maximum, minimum and mean values.
|
86
|
+
You must have called Survey.weather() at least once before to get the summary.
|
81
87
|
|
82
88
|
### Other methods
|
83
89
|
### Known issues
|
@@ -3,10 +3,14 @@ import numpy as np
|
|
3
3
|
from .weather import WeatherHistory
|
4
4
|
|
5
5
|
|
6
|
-
DECIMALS=
|
6
|
+
DECIMALS=1
|
7
|
+
|
8
|
+
pd.set_option('display.max_columns', None)
|
9
|
+
pd.set_option('display.max_rows', None)
|
10
|
+
|
11
|
+
#survey.leq_spectra() bug
|
12
|
+
#TODO: C:\Users\tonyr\PycharmProjects\pycoustic\.venv1\Lib\site-packages\pycoustic\survey.py:287: FutureWarning: The behavior of pd.concat with len(keys) != len(objs) is deprecated. In a future version this will raise instead of truncating to the smaller of the two sequences combi = pd.concat(all_pos, axis=1, keys=["UA1", "UA2"])
|
7
13
|
|
8
|
-
# pd.set_option('display.max_columns', None)
|
9
|
-
# pd.set_option('display.max_rows', None)
|
10
14
|
|
11
15
|
class Survey:
|
12
16
|
"""
|
@@ -19,6 +23,7 @@ class Survey:
|
|
19
23
|
def __init__(self):
|
20
24
|
self._logs = {}
|
21
25
|
self._weather = WeatherHistory()
|
26
|
+
self._weatherhist = None
|
22
27
|
|
23
28
|
def _insert_multiindex(self, df=None, super=None, name1="Position", name2="Date"):
|
24
29
|
subs = df.index.to_list() # List of subheaders (dates)
|
@@ -95,6 +100,7 @@ class Survey:
|
|
95
100
|
:return: A dataframe presenting a summary of the Leq and Lmax values requested.
|
96
101
|
"""
|
97
102
|
combi = pd.DataFrame()
|
103
|
+
period_headers = []
|
98
104
|
if leq_cols is None:
|
99
105
|
leq_cols = [("Leq", "A")]
|
100
106
|
if max_cols is None:
|
@@ -194,6 +200,15 @@ class Survey:
|
|
194
200
|
return combi
|
195
201
|
|
196
202
|
def counts(self, cols=None, day_t="60min", evening_t="60min", night_t="15min"):
|
203
|
+
"""
|
204
|
+
Returns counts for each time period. For example, this can return the number of L90 occurrences at each decibel
|
205
|
+
level for daytime and night-time periods.
|
206
|
+
:param cols: Which columns to consider. Default (L90, A).
|
207
|
+
:param day_t: Daytime averaging period. Default 60min.
|
208
|
+
:param evening_t: Evening average period. Default 60min.
|
209
|
+
:param night_t: Night-time average period. Default 15min.
|
210
|
+
:return: Returns a dataframe of counts for each time period.
|
211
|
+
"""
|
197
212
|
if cols is None:
|
198
213
|
cols = [("L90", "A")]
|
199
214
|
combi = pd.DataFrame()
|
@@ -222,6 +237,7 @@ class Survey:
|
|
222
237
|
pos_df = self._insert_multiindex(pos_df, super=key)
|
223
238
|
combi = pd.concat([combi, pos_df], axis=0)
|
224
239
|
combi = self._insert_header(df=combi, new_head_list=period_headers, header_idx=0)
|
240
|
+
#TODO: This dataframe needs tidying.
|
225
241
|
return combi
|
226
242
|
|
227
243
|
def lmax_spectra(self, n=10, t="2min", period="nights"):
|
@@ -253,8 +269,6 @@ class Survey:
|
|
253
269
|
combi = pd.concat(objs=[combi, summary], axis=0)
|
254
270
|
return combi
|
255
271
|
|
256
|
-
# TODO: get_lowest_l90
|
257
|
-
|
258
272
|
def leq_spectra(self, leq_cols=None):
|
259
273
|
"""
|
260
274
|
Compute Leqs over daytime, evening and night-time periods.
|
@@ -263,7 +277,7 @@ class Survey:
|
|
263
277
|
For all Leq columns, use ["Leq"]. For specific columns, use list of tuples [("Leq", "A"), ("Leq", 125)]
|
264
278
|
:return: A dataframe with a continuous Leq computation across dates, for each time period.
|
265
279
|
"""
|
266
|
-
#TODO: C:\Users\tonyr\PycharmProjects\
|
280
|
+
#TODO: C:\Users\tonyr\PycharmProjects\pycoustic\tests.py:674: FutureWarning: The behavior of pd.concat with len(keys) != len(objs) is deprecated. In a future version this will raise instead of truncating to the smaller of the two sequences combi = pd.concat(all_pos, axis=1, keys=["UA1", "UA2"])
|
267
281
|
all_pos = []
|
268
282
|
if leq_cols is None:
|
269
283
|
leq_cols = ["Leq"]
|
@@ -293,55 +307,23 @@ class Survey:
|
|
293
307
|
ends = [self._logs[key].get_end() for key in self._logs.keys()]
|
294
308
|
return min(starts), max(ends)
|
295
309
|
|
296
|
-
def weather(self, interval=6, api_key=
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
self.
|
301
|
-
|
310
|
+
def weather(self, interval=6, api_key=None, country="GB", postcode="WC1", tz="", recompute=False,
|
311
|
+
drop_cols=None):
|
312
|
+
if drop_cols is None:
|
313
|
+
drop_cols = ["sunrise", "sunset", "feels_like", "dew_point", "visibility"]
|
314
|
+
if self._weatherhist is not None and recompute==False:
|
315
|
+
return self._weatherhist
|
316
|
+
else:
|
317
|
+
if api_key is None:
|
318
|
+
raise ValueError("api_key is required")
|
319
|
+
start, end = self.get_start_end()
|
320
|
+
self._weather.reinit(start=start, end=end, interval=interval, api_key=api_key, country=country,
|
321
|
+
postcode=postcode, tz=tz, units="metric")
|
322
|
+
self._weatherhist = self._weather.compute_weather_history(drop_cols=drop_cols)
|
323
|
+
return self._weatherhist
|
302
324
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
# :param leq_cols: List of strings or List of Tuples.
|
309
|
-
# For all Leq columns, use ["Leq"]. For specific columns, use list of tuples [("Leq", "A"), ("Leq", 125)]
|
310
|
-
# :return: A dataframe with a continuous Leq computation across dates, for each time period.
|
311
|
-
# """
|
312
|
-
# combi = pd.DataFrame()
|
313
|
-
# if leq_cols is None:
|
314
|
-
# leq_cols = ["Leq"]
|
315
|
-
# for key in self._logs.keys():
|
316
|
-
# log = self._logs[key]
|
317
|
-
# combined_list = []
|
318
|
-
# # Day
|
319
|
-
# days = log._get_period(data=log.get_antilogs(), period="days")
|
320
|
-
# days = days[leq_cols].apply(lambda x: np.round(10*np.log10(np.mean(x)), DECIMALS))
|
321
|
-
# #days.sort_index(inplace=True)
|
322
|
-
# combined_list.append(days)
|
323
|
-
# period_headers = ["Daytime" for i in range(len(leq_cols))]
|
324
|
-
# # Evening
|
325
|
-
# if log.is_evening():
|
326
|
-
# evenings = log._get_period(data=log.get_antilogs(), period="evenings")
|
327
|
-
# evenings = evenings[leq_cols].apply(lambda x: np.round(10*np.log10(np.mean(x)), DECIMALS))
|
328
|
-
# evenings.sort_index(inplace=True)
|
329
|
-
# combined_list.append(evenings)
|
330
|
-
# for i in range(len(leq_cols)):
|
331
|
-
# period_headers.append("Evening")
|
332
|
-
# # Night Leq
|
333
|
-
# nights = log._get_period(data=log.get_antilogs(), period="nights")
|
334
|
-
# nights = nights[leq_cols].apply(lambda x: np.round(10*np.log10(np.mean(x)), DECIMALS))
|
335
|
-
# nights.sort_index(inplace=True)
|
336
|
-
# combined_list.append(nights)
|
337
|
-
# for i in range(len(leq_cols)):
|
338
|
-
# period_headers.append("Night-time")
|
339
|
-
# summary = pd.concat(objs=combined_list, axis=1)
|
340
|
-
# summary = self._insert_multiindex(df=summary, super=key)
|
341
|
-
# combi = pd.concat(objs=[combi, summary], axis=0)
|
342
|
-
# new_head_dict = {}
|
343
|
-
# for i in range(len(period_headers)):
|
344
|
-
# new_head_dict[i] = period_headers[i]
|
345
|
-
# combi.rename(columns=new_head_dict, inplace=True)
|
346
|
-
# #combi = combi.transpose()
|
347
|
-
# return combi
|
325
|
+
def weather_summary(self):
|
326
|
+
if self._weatherhist is None:
|
327
|
+
raise ValueError("No weather history available. Use Survey.weather() first.")
|
328
|
+
return pd.DataFrame([self._weatherhist.min(), self._weatherhist.max(), self._weatherhist.mean()],
|
329
|
+
index=["Min", "Max", "Mean"]).drop(columns=["dt"]).round(decimals=1)
|
@@ -10,14 +10,14 @@ from tkinter import StringVar
|
|
10
10
|
#import matplotlib
|
11
11
|
#from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
12
12
|
|
13
|
-
#from
|
13
|
+
#from pycoustic import log # Assuming log is an instance of a class with the required methods
|
14
14
|
test = 0
|
15
15
|
|
16
16
|
class Application(tk.Tk):
|
17
17
|
|
18
18
|
def __init__(self):
|
19
19
|
super().__init__()
|
20
|
-
self.title("
|
20
|
+
self.title("pycoustic Log Viewer")
|
21
21
|
self.geometry("1200x800")
|
22
22
|
import tkinter as tk
|
23
23
|
|
@@ -2,11 +2,10 @@ import requests
|
|
2
2
|
import pandas as pd
|
3
3
|
import datetime as dt
|
4
4
|
|
5
|
-
test=0
|
6
5
|
|
7
6
|
appid = ""
|
8
|
-
with open("tests/openweather_app_id.txt") as f:
|
9
|
-
|
7
|
+
# with open("tests/openweather_app_id.txt") as f:
|
8
|
+
# appid = f.readlines()[0]
|
10
9
|
|
11
10
|
w_dict = {
|
12
11
|
"start": "2022-09-16 12:00:00",
|
@@ -18,6 +17,7 @@ w_dict = {
|
|
18
17
|
"tz": "GB"
|
19
18
|
}
|
20
19
|
|
20
|
+
|
21
21
|
def test_weather_obj(weather_test_dict):
|
22
22
|
hist = WeatherHistory(start=w_dict["start"], end=w_dict["end"], interval=w_dict["interval"],
|
23
23
|
api_key=w_dict["api_key"], country=w_dict["country"], postcode=w_dict["postcode"],
|
@@ -58,7 +58,6 @@ class WeatherHistory:
|
|
58
58
|
base = "https://api.openweathermap.org/data/3.0/onecall/timemachine?"
|
59
59
|
query = str(base + "lat=" + str(self._lat) + "&" + "lon=" + str(self._lon) + "&" + "units=" + self._units + \
|
60
60
|
"&" + "dt=" + str(timestamp) + "&" + "appid=" + self._api_key)
|
61
|
-
print(query)
|
62
61
|
return query
|
63
62
|
|
64
63
|
def _construct_timestamps(self):
|
@@ -71,28 +70,25 @@ class WeatherHistory:
|
|
71
70
|
|
72
71
|
def _make_and_parse_api_call(self, query):
|
73
72
|
response = requests.get(query)
|
74
|
-
print(response.json())
|
75
73
|
# This drops some unwanted cols like lat, lon, timezone and tz offset.
|
76
74
|
resp_dict = response.json()["data"][0]
|
77
75
|
del resp_dict["weather"] # delete weather key as not useful.
|
78
76
|
# TODO: parse 'weather' nested dict.
|
79
77
|
return resp_dict
|
80
78
|
|
81
|
-
def compute_weather_history(self):
|
79
|
+
def compute_weather_history(self, drop_cols):
|
82
80
|
# construct timestamps
|
83
81
|
timestamps = self._construct_timestamps()
|
84
82
|
# make calls to API
|
85
83
|
responses = []
|
86
84
|
for ts in timestamps:
|
87
|
-
print(f"ts: {ts}")
|
88
85
|
query = self._construct_api_call(timestamp=ts)
|
89
86
|
response_dict = self._make_and_parse_api_call(query=query)
|
90
87
|
responses.append(pd.Series(response_dict))
|
91
88
|
df = pd.concat(responses, axis=1).transpose()
|
92
89
|
for col in ["dt", "sunrise", "sunset"]:
|
93
90
|
df[col] = df[col].apply(lambda x: dt.datetime.fromtimestamp(int(x))) # convert timestamp into datetime
|
94
|
-
|
95
|
-
self._hist = df
|
91
|
+
df.drop(columns=drop_cols, inplace=True)
|
96
92
|
return df
|
97
93
|
|
98
94
|
def get_weather_history(self):
|
@@ -1,16 +1,18 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "pycoustic"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.7"
|
4
4
|
description = ""
|
5
5
|
authors = ["thumpercastle <tony.ryb@gmail.com>"]
|
6
6
|
readme = "README.md"
|
7
7
|
|
8
8
|
[tool.poetry.dependencies]
|
9
|
-
python = ">=3.
|
9
|
+
python = ">=3.11.9, <4.0"
|
10
10
|
pandas = "2.2.3"
|
11
11
|
numpy = "2.2.6"
|
12
12
|
openpyxl = "3.1.5"
|
13
13
|
requests = "^2.32.4"
|
14
|
+
plotly = "6.1.2"
|
15
|
+
streamlit = "^1.46.1"
|
14
16
|
|
15
17
|
|
16
18
|
[build-system]
|
File without changes
|