DASPy-toolbox 1.1.5__tar.gz → 1.2.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.
Files changed (34) hide show
  1. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/DASPy_toolbox.egg-info/PKG-INFO +1 -1
  2. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/DASPy_toolbox.egg-info/SOURCES.txt +1 -0
  3. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/PKG-INFO +1 -1
  4. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/advanced_tools/channel.py +17 -18
  5. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/basic_tools/freqattributes.py +34 -2
  6. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/basic_tools/preprocessing.py +144 -31
  7. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/basic_tools/visualization.py +29 -16
  8. daspy_toolbox-1.2.0/daspy/core/collection.py +480 -0
  9. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/core/dasdatetime.py +11 -5
  10. daspy_toolbox-1.2.0/daspy/core/read.py +657 -0
  11. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/core/section.py +192 -118
  12. daspy_toolbox-1.2.0/daspy/core/util.py +140 -0
  13. daspy_toolbox-1.2.0/daspy/core/write.py +670 -0
  14. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/setup.py +1 -1
  15. daspy_toolbox-1.1.5/daspy/core/collection.py +0 -323
  16. daspy_toolbox-1.1.5/daspy/core/read.py +0 -569
  17. daspy_toolbox-1.1.5/daspy/core/write.py +0 -295
  18. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/DASPy_toolbox.egg-info/dependency_links.txt +0 -0
  19. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/DASPy_toolbox.egg-info/entry_points.txt +0 -0
  20. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/DASPy_toolbox.egg-info/requires.txt +0 -0
  21. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/DASPy_toolbox.egg-info/top_level.txt +0 -0
  22. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/LICENSE +0 -0
  23. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/README.md +0 -0
  24. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/__init__.py +0 -0
  25. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/advanced_tools/__init__.py +0 -0
  26. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/advanced_tools/decomposition.py +0 -0
  27. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/advanced_tools/denoising.py +0 -0
  28. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/advanced_tools/fdct.py +0 -0
  29. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/advanced_tools/strain2vel.py +0 -0
  30. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/basic_tools/__init__.py +0 -0
  31. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/basic_tools/filter.py +0 -0
  32. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/core/__init__.py +0 -0
  33. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/daspy/core/example.pkl +0 -0
  34. {daspy_toolbox-1.1.5 → daspy_toolbox-1.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DASPy-toolbox
3
- Version: 1.1.5
3
+ Version: 1.2.0
4
4
  Summary: DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing, which comprises classic seismic data processing techniques and Specialized algorithms for DAS applications.
5
5
  Home-page: https://github.com/HMZ-03/DASPy
6
6
  Author: Minzhe Hu, Zefeng Li
@@ -25,4 +25,5 @@ daspy/core/dasdatetime.py
25
25
  daspy/core/example.pkl
26
26
  daspy/core/read.py
27
27
  daspy/core/section.py
28
+ daspy/core/util.py
28
29
  daspy/core/write.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DASPy-toolbox
3
- Version: 1.1.5
3
+ Version: 1.2.0
4
4
  Summary: DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing, which comprises classic seismic data processing techniques and Specialized algorithms for DAS applications.
5
5
  Home-page: https://github.com/HMZ-03/DASPy
6
6
  Author: Minzhe Hu, Zefeng Li
@@ -365,24 +365,6 @@ def turning_points(data, data_type='coordinate', thresh=5, depth_info=False,
365
365
  raise ValueError('Data_type should be \'coordinate\' or \'waveform\'.')
366
366
 
367
367
 
368
- def _equally_spacing(dist, dx):
369
- index = [[], []]
370
- residual = [0, abs(dist[0]-dx)]
371
- for i in range(2, len(dist)+1):
372
- res = []
373
- for j in range(i):
374
- res.append(residual[j] + abs(dx - sum(dist[j:i])))
375
- residual.append(min(res))
376
- k = np.argmin(res)
377
- if k > 0:
378
- index.append(index[k] + [k])
379
- else:
380
- index.append(index[k])
381
- # print(index, residual)
382
-
383
- return index[-1]
384
-
385
-
386
368
  def channel_spacing(geometry, depth_info=False):
387
369
  nch = len(geometry)
388
370
  dist = np.zeros(nch - 1)
@@ -456,6 +438,23 @@ def closest_channel_to_point(geometry, points, verbose=False):
456
438
  return channels[closest_index]
457
439
 
458
440
 
441
+ def _equally_spacing(dist, dx):
442
+ index = [[], []]
443
+ residual = [0, abs(dist[0]-dx)]
444
+ for i in range(2, len(dist)+1):
445
+ res = []
446
+ for j in range(i):
447
+ res.append(residual[j] + abs(dx - sum(dist[j:i])))
448
+ residual.append(min(res))
449
+ k = np.argmin(res)
450
+ if k > 0:
451
+ index.append(index[k] + [k])
452
+ else:
453
+ index.append(index[k])
454
+
455
+ return index[-1]
456
+
457
+
459
458
  def equally_spaced_channels(geometry, dx, depth_info=False, verbose=False):
460
459
  """
461
460
  Find equally spaced channel numbers based on known DAS latitude and
@@ -1,10 +1,10 @@
1
1
  # Purpose: Analyze frequency attribute and transform in frequency domain
2
2
  # Author: Minzhe Hu
3
- # Date: 2024.6.8
3
+ # Date: 2024.6.17
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import numpy as np
6
6
  from numpy.fft import rfft, rfft2, fftshift, fftfreq, rfftfreq
7
- from scipy.signal import stft
7
+ from scipy.signal import stft, welch
8
8
  from daspy.basic_tools.preprocessing import demeaning, detrending, cosine_taper
9
9
 
10
10
 
@@ -47,6 +47,38 @@ def spectrum(data, fs, taper=0.05, nfft='default'):
47
47
  return spec, f
48
48
 
49
49
 
50
+ def psd(data, fs, nperseg=256, noverlap=None, nfft=None, detrend=False,
51
+ average='mean'):
52
+ """
53
+ Computes the power spectral density of the given data.
54
+
55
+ :param data: numpy.ndarray. Data to make spectrum of.
56
+ :param fs: Sampling rate in Hz.
57
+ :param nperseg: int. Length of each segment. Defaults to None, but if window
58
+ is str or tuple, is set to 256, and if window is array_like, is set to
59
+ the length of the window.
60
+ :param noverlap: int. Number of points to overlap between segments. If None,
61
+ noverlap = nperseg // 2. Defaults to None.
62
+ :param nfft: int. Length of the FFT used, if a zero padded FFT is desired.
63
+ If None, the FFT length is nperseg. Defaults to None.
64
+ :param detrend: str or bool. Specifies whether and how to detrend each
65
+ segment. 'linear' or 'detrend' or True = detrend, 'constant' or
66
+ 'demean' = demean.
67
+ :param average: 'mean' or 'median. Method to use when averaging
68
+ periodograms. Defaults to 'mean'.
69
+ :return: Power spectral density or power spectrum and array of sample
70
+ frequencies.
71
+ """
72
+ if len(data.shape) == 1:
73
+ data = data.reshape(1, len(data))
74
+ elif len(data.shape) != 2:
75
+ raise ValueError("Data should be 1-D or 2-D array")
76
+
77
+ f, psd = welch(data, fs=fs, nperseg=nperseg, noverlap=noverlap,
78
+ nfft=nfft, detrend=detrend, axis=1, average=average)
79
+ return psd, f
80
+
81
+
50
82
  def spectrogram(data, fs, nperseg=256, noverlap=None, nfft=None, detrend=False,
51
83
  boundary='zeros'):
52
84
  """
@@ -1,7 +1,8 @@
1
1
  # Purpose: Some preprocess methods
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.3.10
3
+ # Date: 2025.6.25
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
+ import warnings
5
6
  import numpy as np
6
7
  from scipy.signal import detrend
7
8
  from scipy.signal.windows import tukey
@@ -89,11 +90,11 @@ def stacking(data: np.ndarray, N: int, step: int = None, average: bool = True):
89
90
  return data
90
91
  if step is None:
91
92
  step = N
92
- nch, nt = data.shape
93
+ nch, nsp = data.shape
93
94
  begin = np.arange(0, nch - N + 1, step)
94
95
  end = begin + N
95
96
  nx1 = len(begin)
96
- data_stacked = np.zeros((nx1, nt))
97
+ data_stacked = np.zeros((nx1, nsp))
97
98
  for i in range(nx1):
98
99
  data_stacked[i, :] = np.sum(data[begin[i]:end[i], :], axis=0)
99
100
  if average:
@@ -115,9 +116,9 @@ def cosine_taper(data, p=0.1, side='both'):
115
116
  """
116
117
  if data.ndim == 1:
117
118
  data = data.reshape(1, -1)
118
- nch, nt = data.shape
119
+ nch, nsp = data.shape
119
120
  if not isinstance(p, (tuple, list, np.ndarray)):
120
- win = tukey(nt, p)
121
+ win = tukey(nsp, p)
121
122
  if side == 'left':
122
123
  win[round(nch/2):] = 1
123
124
  elif side == 'right':
@@ -125,7 +126,7 @@ def cosine_taper(data, p=0.1, side='both'):
125
126
  return data * np.tile(win, (nch, 1))
126
127
  else:
127
128
  if p[0] > 0:
128
- data = data * np.tile(tukey(nch, p[0]), (nt, 1)).T
129
+ data = data * np.tile(tukey(nch, p[0]), (nsp, 1)).T
129
130
  return cosine_taper(data, p[1], side=side)
130
131
 
131
132
 
@@ -145,43 +146,155 @@ def downsampling(data, xint=None, tint=None, stack=True, lowpass_filter=True):
145
146
  if stack:
146
147
  data_ds = stacking(data, xint)
147
148
  else:
148
- data_ds = data_ds[::xint]
149
+ data_ds = data_ds[::xint].copy()
149
150
  if tint and tint > 1:
150
151
  if lowpass_filter:
151
152
  data_ds = lowpass_cheby_2(data_ds, 1, 1 / 2 / tint)
152
153
  if len(data_ds.shape) == 1:
153
- data_ds = data_ds[::tint]
154
+ data_ds = data_ds[::tint].copy()
154
155
  else:
155
- data_ds = data_ds[:, ::tint]
156
+ data_ds = data_ds[:, ::tint].copy()
156
157
  return data_ds
157
158
 
158
159
 
159
- def trimming(data, dx=None, fs=None, xmin=0, xmax=None, tmin=0, tmax=None,
160
- mode=0):
160
+ def _trimming_index(nch, nsp, dx=None, fs=None, start_channel=0,
161
+ start_distance=0, start_time=0, xmin=None, xmax=None,
162
+ chmin=None, chmax=None, tmin=None, tmax=None, spmin=None,
163
+ spmax=None):
164
+ assert None in [tmin, spmin], \
165
+ "Please do not set tmin and spmin at the same time."
166
+ assert None in [tmax, spmax], \
167
+ "Please do not set tmax and spmax at the same time."
168
+ assert None in [xmin, chmin], \
169
+ "Please do not set xmin and chmin at the same time."
170
+ assert None in [xmax, chmax], \
171
+ "Please do not set xmax and chmax at the same time."
172
+ if dx is None:
173
+ assert xmin is None and xmax is None, "Please set dx"
174
+ if fs is None:
175
+ assert tmin is None and tmax is None, "Please set fs"
176
+
177
+ if xmin is None:
178
+ if chmin is None:
179
+ i0 = 0
180
+ else:
181
+ i0 = int(chmin - start_channel)
182
+ if i0 < 0:
183
+ warnings.warn('chmin < start_channel . Set chmin to '
184
+ 'start_channel.')
185
+ i0 = 0
186
+ elif i0 >= nch:
187
+ raise ValueError('chmin >= end_channel.')
188
+ else:
189
+ i0 = round((xmin - start_distance) / dx)
190
+ if i0 < 0:
191
+ warnings.warn('xmin is smaller than start_distance. Set xmin '
192
+ 'to 0.')
193
+ i0 = 0
194
+ elif i0 >= nch:
195
+ raise ValueError('xmin is later than end_distance.')
196
+
197
+ if xmax is None:
198
+ if chmax is None:
199
+ i1 = nch
200
+ else:
201
+ i1 = int(chmax - start_channel)
202
+ if i1 <= 0:
203
+ raise ValueError('chmax <= start_channel.')
204
+ elif i1 > nch:
205
+ warnings.warn('chmax > end_channel. Set chmax to '
206
+ 'end_channel.')
207
+ i1 = nch
208
+ else:
209
+ i1 = round((xmax - start_distance) / dx)
210
+ if i1 <= 0:
211
+ raise ValueError('xmax is smaller than start_distance.')
212
+ if i1 > nch:
213
+ warnings.warn('xmax is later than end_distance. Set xmax '
214
+ 'to the array length.')
215
+ i1 = nch
216
+
217
+ if tmin is None:
218
+ if spmin is None:
219
+ j0 = 0
220
+ else:
221
+ j0 = int(spmin)
222
+ if j0 < 0:
223
+ warnings.warn('spmin < 0. Set spmin to 0.')
224
+ j0 = 0
225
+ elif j0 >= nsp:
226
+ raise ValueError('spmin > nsp.')
227
+ else:
228
+ try:
229
+ j0 = round((tmin - start_time) * fs)
230
+ except TypeError:
231
+ j0 = round(tmin * fs)
232
+ if j0 < 0:
233
+ warnings.warn('tmin is earlier than start_time. Set tmin '
234
+ 'to start_time.')
235
+ j0 = 0
236
+ elif j0 >= nsp:
237
+ raise ValueError('tmin is later than end_time.')
238
+
239
+ if tmax is None:
240
+ if spmax is None:
241
+ j1 = nsp
242
+ else:
243
+ j1 = int(spmax)
244
+ if j1 <= 0:
245
+ raise ValueError('spmax < 0.')
246
+ elif j1 > nsp:
247
+ warnings.warn('spmax > nsp. Set spmax to nsp.')
248
+ j1 = nsp
249
+ else:
250
+ try:
251
+ j1 = round((tmax - start_time) * fs)
252
+ except TypeError:
253
+ j1 = round(tmax * fs)
254
+ if j1 <= 0:
255
+ raise ValueError('tmax is earlier than start_time.')
256
+ if j1 > nsp:
257
+ warnings.warn('tmax is later than end_time. Set tmax to the'
258
+ ' end_time.')
259
+ j1 = nsp
260
+ return i0, i1, j0, j1
261
+
262
+
263
+ def trimming(data, dx=None, fs=None, xmin=None, xmax=None, chmin=None,
264
+ chmax=None, tmin=None, tmax=None, spmin=None, spmax=None,
265
+ **kwargs):
161
266
  """
162
267
  Cut data to given start and end distance/channel or time/sampling points.
163
268
 
164
269
  :param data: numpy.ndarray. Data to trim can be 1-D or 2-D.
165
270
  :param dx: Channel interval in m.
166
271
  :param fs: Sampling rate in Hz.
167
- :param xmin, xmax, tmin, tmax: Boundary for trimming.
168
- :param mode: 0 means the unit of boundary is channel number and sampling
169
- points; 1 means the unit of boundary is meters and seconds.
272
+ :param xmin, xmax: float. Range of distance.
273
+ :param chmin, chmax: int. Channel number range.
274
+ :param tmin, tmax: float or DASDateTime. Range of time.
275
+ :param spmin, spmax: int. Sampling point range.
170
276
  :return: Trimmed data.
171
277
  """
172
- nch, nt = data.shape
173
- if mode == 0:
174
- if xmax is None:
175
- xmax = nch
176
- if tmax is None:
177
- tmax = nt
178
- elif mode == 1:
179
- xmin = round(xmin / dx)
180
- xmax = (round(xmax / dx), nch)[xmax is None]
181
- tmin = round(tmin * fs)
182
- tmax = (round(tmax * fs), nt)[tmax is None]
183
-
184
- return data[xmin:xmax, tmin:tmax]
278
+ # Compatible with old interfaces and remind users
279
+ if 'mode' in kwargs:
280
+ warnings.warn('In future versions, the mode parameter will be '
281
+ 'deprecated. xmin/xmax will only control the distance'
282
+ ' range, tmin/tmax will only control the time range; '
283
+ 'please use chmin/chmax to control the channel number'
284
+ ' range, and spmin/spmax to control the sampling '
285
+ 'point range', FutureWarning)
286
+ if kwargs['mode'] == 0:
287
+ chmin, chmax = xmin, xmax
288
+ xmin, xmax = None, None
289
+ spmin, spmax = tmin, tmax
290
+ tmin, tmax = None, None
291
+ nch, nsp = data.shape
292
+ i0, i1, j0, j1 = _trimming_index(nch, nsp, dx=dx, fs=fs, xmin=xmin,
293
+ xmax=xmax, chmin=chmin, chmax=chmax,
294
+ tmin=tmin, tmax=tmax, spmin=spmin,
295
+ spmax=spmax)
296
+
297
+ return data[i0:i1, j0:j1].copy()
185
298
 
186
299
 
187
300
  def padding(data, dn, reverse=False):
@@ -194,16 +307,16 @@ def padding(data, dn, reverse=False):
194
307
  :param reverse: bool. Set True to reverse the operation.
195
308
  :return: Padded data.
196
309
  """
197
- nch, nt = data.shape
310
+ nch, nsp = data.shape
198
311
  if isinstance(dn, int):
199
312
  dn = (dn, dn)
200
313
 
201
314
  pad = (dn[0] // 2, dn[0] - dn[0] // 2, dn[1] // 2, dn[1] - dn[1] // 2)
202
315
  if reverse:
203
- return data[pad[0]:nch - pad[1], pad[2]:nt - pad[3]]
316
+ return data[pad[0]:nch - pad[1], pad[2]:nsp - pad[3]]
204
317
  else:
205
- data_pd = np.zeros((nch + dn[0], nt + dn[1]))
206
- data_pd[pad[0]:nch + pad[0], pad[2]:nt + pad[2]] = data
318
+ data_pd = np.zeros((nch + dn[0], nsp + dn[1]))
319
+ data_pd[pad[0]:nch + pad[0], pad[2]:nsp + pad[2]] = data
207
320
  return data_pd
208
321
 
209
322
 
@@ -1,6 +1,6 @@
1
1
  # Purpose: Plot data
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.1.6
3
+ # Date: 2025.6.17
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import numpy as np
6
6
  import matplotlib.pyplot as plt
@@ -9,11 +9,11 @@ from collections.abc import Sequence
9
9
 
10
10
  def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
11
11
  title=None, transpose=False, t0=0, x0=0, pick=None, f=None, k=None,
12
- t=None, c=None, cmap=None, vmin=None, vmax=None, dB=False,
13
- xmode='distance', tmode='time', xlim=None, ylim=None, xlog=False,
14
- ylog=False, xinv=False, yinv=False, xlabel=True, ylabel=True,
15
- xticklabels=True, yticklabels=True, colorbar=True, colorbar_label=None,
16
- savefig=None):
12
+ t=None, c=None, cmap=None, vmin=None, vmin_per=None, vmax=None,
13
+ vmax_per=None, dB=False, xmode='distance', tmode='time', xlim=None,
14
+ ylim=None, xlog=False, ylog=False, xinv=False, yinv=False, xlabel=True,
15
+ ylabel=True, xticklabels=True, yticklabels=True, colorbar=True,
16
+ colorbar_label=None, savefig=None):
17
17
  """
18
18
  Plot several types of 2-D seismological data.
19
19
 
@@ -24,7 +24,7 @@ def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
24
24
  figsize. If not specified, the function will directly display the image
25
25
  using matplotlib.pyplot.show().
26
26
  :param obj: str. Type of data to plot. It should be one of 'waveform',
27
- 'phasepick', 'spectrum', 'spectrogram', 'fk', or 'dispersion'.
27
+ 'phasepick', 'spectrum', 'psd', 'spectrogram', 'fk', or 'dispersion'.
28
28
  :param dpi: int. The resolution of the figure in dots-per-inch.
29
29
  :param title: str. The title of this axes.
30
30
  :param transpose: bool. Transpose the figure or not.
@@ -33,13 +33,15 @@ def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
33
33
  P phase, 'S' for S phase and 'N' for unknown phase type. Required if
34
34
  obj=='phasepick'.
35
35
  :param f: Sequence of frequency. Required if obj is one of 'spectrum',
36
- 'spectrogram', 'fk' or 'dispersion'.
36
+ 'psd', 'spectrogram', 'fk' or 'dispersion'.
37
37
  :param k: Wavenumber sequence. Required if obj=='fk'.
38
38
  :param t: Time sequence. Required if obj=='spectrogram'.
39
39
  :param c: Phase velocity sequence. Required if obj=='dispersion'.
40
40
  :param cmap: str or Colormap. The Colormap instance or registered colormap
41
41
  name used to map scalar data to colors.
42
42
  :param vmin, vmax: Define the data range that the colormap covers.
43
+ :param vmin_per, vmax_per: float. Define the data range that the colormap
44
+ covers by percentile.
43
45
  :param dB: bool. Transfer data unit to dB and take 1 as the reference value.
44
46
  :param xmode: str. 'distance' or 'channel'.
45
47
  :param tmode: str. 'time' or 'sampling'.
@@ -67,7 +69,9 @@ def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
67
69
 
68
70
  if obj in ['waveform', 'phasepick']:
69
71
  cmap = 'RdBu' if cmap is None else cmap
70
- vmax = np.percentile(abs(data), 80) if vmax is None else vmax
72
+ if vmax is None:
73
+ vmax_per = 80 if vmax_per is None else vmax_per
74
+ vmax = np.percentile(data, vmax_per)
71
75
  vmin = -vmax if vmin is None else vmin
72
76
  origin = 'upper'
73
77
  if fs is None or tmode == 'sampling':
@@ -95,17 +99,26 @@ def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
95
99
  if tmode.lower() == 'sampling':
96
100
  pck[:, 1] = pck[:, 1] / fs
97
101
  ax.scatter(pck[:,0], t0 + pck[:,1], marker=',', s=0.1,
98
- c=pick_color[phase])
102
+ c=pick_color[phase])
99
103
 
100
- elif obj in ['spectrum', 'spectrogram', 'fk', 'dispersion']:
101
- if isinstance(data[0,0], (complex, np.complex64)):
104
+ elif obj in ['spectrum', 'spectrogram', 'psd', 'fk', 'dispersion']:
105
+ if np.iscomplex(data).any():
102
106
  data = abs(data)
103
107
  if dB:
104
108
  data = 20 * np.log10(data)
105
- cmap = 'jet' if cmap is None else cmap
106
- vmax = np.percentile(abs(data), 80) if vmax is None else vmax
107
- vmin = np.percentile(abs(data), 20) if vmin is None else vmin
108
- if obj == 'spectrum':
109
+ if obj == 'psd':
110
+ cmap = 'viridis' if cmap is None else cmap
111
+ else:
112
+ cmap = 'jet' if cmap is None else cmap
113
+
114
+ if vmax is None:
115
+ vmax_per = 80 if vmax_per is None else vmax_per
116
+ vmax = np.percentile(data, vmax_per)
117
+ if vmin is None:
118
+ vmin_per = 20 if vmin_per is None else vmin_per
119
+ vmin = np.percentile(data, vmin_per)
120
+
121
+ if obj in ['spectrum', 'psd']:
109
122
  origin = 'lower'
110
123
  if dx is None or xmode.lower() == 'channel':
111
124
  xlabel_default = 'Channel'