pycoustic 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
1
  from .log import Log
2
2
  from .survey import Survey
3
- from .weather import WeatherHistory
3
+ from .weather import WeatherHistory
4
+
pycoustic/survey.py CHANGED
@@ -3,10 +3,14 @@ import numpy as np
3
3
  from .weather import WeatherHistory
4
4
 
5
5
 
6
- DECIMALS=0
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\src\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"])
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="", country="GB", postcode="WC1", tz="",):
297
- start, end = self.get_start_end()
298
- self._weather.reinit(start=start, end=end, interval=interval, api_key=api_key, country=country,
299
- postcode=postcode, tz=tz, units="metric")
300
- self._weather.compute_weather_history()
301
- return self._weather.get_weather_history()
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
- # def typical_leq_spectra(self, leq_cols=None):
304
- # """
305
- # DEPRECATED 2025/06/05. Replaced by .leq_spectra() **TT**
306
- # Compute Leqs over daytime, evening and night-time periods.
307
- # This is an overall Leq, and does not group Leqs by date.
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)
pycoustic/tkgui.py CHANGED
@@ -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 src import log # Assuming log is an instance of a class with the required methods
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("src Log Viewer")
20
+ self.title("pycoustic Log Viewer")
21
21
  self.geometry("1200x800")
22
22
  import tkinter as tk
23
23
 
pycoustic/weather.py CHANGED
@@ -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
- appid = f.readlines()[0]
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
- print(df)
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,17 +1,19 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pycoustic
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary:
5
5
  Author: thumpercastle
6
6
  Author-email: tony.ryb@gmail.com
7
- Requires-Python: >=3.12,<4.0
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.modal_l90()
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.typical_leq_spectra()
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
@@ -0,0 +1,8 @@
1
+ pycoustic/__init__.py,sha256=jq9Tzc5nEgXh8eNf0AkAypmw3Dda9A-iSy-tyFaTksA,89
2
+ pycoustic/log.py,sha256=HNdS2hKKbUdqY7iAMj9QJqoI9r4ZtJ7GCXnIx8XpTH4,17145
3
+ pycoustic/survey.py,sha256=KTNCt4kV63Dq06RCbh9G9Nl8Frk8NsTt6AhxiMIEixg,17746
4
+ pycoustic/tkgui.py,sha256=YAy5f_qkXZ3yU8BvB-nIVQX1fYwPs_IkwmDEXHPMAa4,13997
5
+ pycoustic/weather.py,sha256=3FIzpp3jniA1SRObMCnKsobVFZxJX5gpugsAWA3bH8o,3751
6
+ pycoustic-0.1.7.dist-info/METADATA,sha256=SEdgTvtyNxGVcgYFKrjwIh0TZw5gUzHoc_GRGaxkykg,8413
7
+ pycoustic-0.1.7.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
8
+ pycoustic-0.1.7.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- pycoustic/__init__.py,sha256=0UGHX-vdulM9x1j8g_DcR_BbtBk0gEFg9l4KjLl4Qrk,85
2
- pycoustic/log.py,sha256=HNdS2hKKbUdqY7iAMj9QJqoI9r4ZtJ7GCXnIx8XpTH4,17145
3
- pycoustic/survey.py,sha256=6bQW1UniUgUdj7jOpqTagdos6IRVPhURegmq9PgBYjQ,18453
4
- pycoustic/tkgui.py,sha256=vvB0D29GER-i7lC_SUuzKrzbh1EFAAhpRaHi7kggtYk,13985
5
- pycoustic/weather.py,sha256=xEp5-ZYlYJXB-G2QQuEETTfyMALAWJtSWtLn5XHDRFo,3822
6
- pycoustic-0.1.5.dist-info/METADATA,sha256=z1v2MSgNEJx2pC9cSqsxudUpLzizuzTxnDBRLk9iBuY,7987
7
- pycoustic-0.1.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
8
- pycoustic-0.1.5.dist-info/RECORD,,