回答編集履歴

5

説明の追記

2020/12/05 02:33

投稿

jbpb0
jbpb0

スコア7651

test CHANGED
@@ -52,7 +52,7 @@
52
52
 
53
53
  [Savitzky-Golay smoothing filter for not equally spaced data](https://dsp.stackexchange.com/questions/1676/savitzky-golay-smoothing-filter-for-not-equally-spaced-data)
54
54
 
55
- に、不等間隔でも計算できるSavitzky–Golay法のPythonコードが載ってたので、それを微分にも使えるように修正しました
55
+ に、不等間隔(non-uniform)でも計算できるSavitzky–Golay法のPythonコードが載ってたので、それを微分(differential)にも使えるように修正しました
56
56
 
57
57
  よろしければお試しください
58
58
 

4

コードのバグ修正

2020/12/05 02:33

投稿

jbpb0
jbpb0

スコア7651

test CHANGED
@@ -60,6 +60,8 @@
60
60
 
61
61
  import numpy as np
62
62
 
63
+ import math
64
+
63
65
 
64
66
 
65
67
  def non_uniform_savgol_der(x, y, window, polynom, der):
@@ -102,7 +104,7 @@
102
104
 
103
105
  der : int
104
106
 
105
- The order of derivative. Must be positive and less than 3
107
+ The order of derivative. Must be positive and smaller than polynom order
106
108
 
107
109
 
108
110
 
@@ -152,9 +154,9 @@
152
154
 
153
155
 
154
156
 
155
- if polynom < 2:
157
+ if polynom < 0:
156
-
158
+
157
- raise ValueError('"polynom" must be larger than 1')
159
+ raise ValueError('"polynom" must be larger than 0')
158
160
 
159
161
 
160
162
 
@@ -170,9 +172,9 @@
170
172
 
171
173
 
172
174
 
173
- if der > 2:
175
+ if der > polynom:
174
-
176
+
175
- raise TypeError('"der" must be less than 3')
177
+ raise TypeError('"der" must be equal or less than "polynom"')
176
178
 
177
179
 
178
180
 
@@ -248,7 +250,7 @@
248
250
 
249
251
  #y_smoothed[i] += coeffs[0, j] * y[i + j - half_window]
250
252
 
251
- y_smoothed[i] += coeffs[der, j] * y[i + j - half_window]
253
+ y_smoothed[i] += coeffs[der, j] * y[i + j - half_window] * math.factorial(der)
252
254
 
253
255
 
254
256
 
@@ -288,7 +290,7 @@
288
290
 
289
291
  for j in range(der, polynom, 1):
290
292
 
291
- y_smoothed[i] += first_coeffs[j] * x_i
293
+ y_smoothed[i] += first_coeffs[j] * x_i * math.factorial(j)
292
294
 
293
295
  x_i *= x[i] - x[half_window]
294
296
 
@@ -306,7 +308,7 @@
306
308
 
307
309
  for j in range(der, polynom, 1):
308
310
 
309
- y_smoothed[i] += last_coeffs[j] * x_i
311
+ y_smoothed[i] += last_coeffs[j] * x_i * math.factorial(j)
310
312
 
311
313
  x_i *= x[i] - x[-half_window - 1]
312
314
 
@@ -326,9 +328,9 @@
326
328
 
327
329
  # テスト用ダミーデータ
328
330
 
329
- x = np.arange(0, 100)
331
+ x = np.arange(0, 10, 0.1)
330
-
332
+
331
- y = np.arange(10, 110) + np.random.randn(100) * 2
333
+ y = 2 * x * x + 3 * x + 4 + np.random.randn(100) / 10
332
334
 
333
335
  y11 = non_uniform_savgol_der(x, y, 11, 2, 0)
334
336
 

3

コード追加

2020/12/02 14:14

投稿

jbpb0
jbpb0

スコア7651

test CHANGED
@@ -367,3 +367,49 @@
367
367
  plt.show()
368
368
 
369
369
  ```
370
+
371
+
372
+
373
+
374
+
375
+ xが順番に並んでないのは、sortすれば直せます
376
+
377
+ ```python
378
+
379
+ # テスト用ダミーデータ
380
+
381
+ x = np.array([1, 2, 3, 5, 4, 6])
382
+
383
+ y = x * 2
384
+
385
+ print(x)
386
+
387
+ print(y)
388
+
389
+
390
+
391
+ # ソート (xの順番でyも)
392
+
393
+ tmp = zip(x, y)
394
+
395
+ tmp2 = sorted(tmp)
396
+
397
+ xx, yy = zip(*tmp2)
398
+
399
+ xx = np.array(xx)
400
+
401
+ yy = np.array(yy)
402
+
403
+
404
+
405
+ # 比較
406
+
407
+ print(x)
408
+
409
+ print(xx)
410
+
411
+ print(y)
412
+
413
+ print(yy)
414
+
415
+ ```

2

コード追加

2020/12/02 04:58

投稿

jbpb0
jbpb0

スコア7651

test CHANGED
@@ -45,3 +45,325 @@
45
45
  データ見たら等間隔ではないので、使えませんね
46
46
 
47
47
  失礼しました
48
+
49
+
50
+
51
+ 【追記】
52
+
53
+ [Savitzky-Golay smoothing filter for not equally spaced data](https://dsp.stackexchange.com/questions/1676/savitzky-golay-smoothing-filter-for-not-equally-spaced-data)
54
+
55
+ に、不等間隔でも計算できるSavitzky–Golay法のPythonコードが載ってたので、それを微分にも使えるように修正しました
56
+
57
+ よろしければお試しください
58
+
59
+ ```python
60
+
61
+ import numpy as np
62
+
63
+
64
+
65
+ def non_uniform_savgol_der(x, y, window, polynom, der):
66
+
67
+ """
68
+
69
+ Applies a Savitzky-Golay filter to y with non-uniform spacing
70
+
71
+ as defined in x
72
+
73
+
74
+
75
+ This is based on https://dsp.stackexchange.com/questions/1676/savitzky-golay-smoothing-filter-for-not-equally-spaced-data
76
+
77
+ The borders are interpolated like scipy.signal.savgol_filter would do
78
+
79
+
80
+
81
+ Parameters
82
+
83
+ ----------
84
+
85
+ x : array_like
86
+
87
+ List of floats representing the x values of the data
88
+
89
+ y : array_like
90
+
91
+ List of floats representing the y values. Must have same length
92
+
93
+ as x
94
+
95
+ window : int (odd)
96
+
97
+ Window length of datapoints. Must be odd and smaller than x
98
+
99
+ polynom : int
100
+
101
+ The order of polynom used. Must be smaller than the window size
102
+
103
+ der : int
104
+
105
+ The order of derivative. Must be positive and less than 3
106
+
107
+
108
+
109
+ Returns
110
+
111
+ -------
112
+
113
+ np.array of float
114
+
115
+ The smoothed y values
116
+
117
+ """
118
+
119
+ if len(x) != len(y):
120
+
121
+ raise ValueError('"x" and "y" must be of the same size')
122
+
123
+
124
+
125
+ if len(x) < window:
126
+
127
+ raise ValueError('The data size must be larger than the window size')
128
+
129
+
130
+
131
+ if type(window) is not int:
132
+
133
+ raise TypeError('"window" must be an integer')
134
+
135
+
136
+
137
+ if window % 2 == 0:
138
+
139
+ raise ValueError('The "window" must be an odd integer')
140
+
141
+
142
+
143
+ if type(polynom) is not int:
144
+
145
+ raise TypeError('"polynom" must be an integer')
146
+
147
+
148
+
149
+ if polynom >= window:
150
+
151
+ raise ValueError('"polynom" must be less than "window"')
152
+
153
+
154
+
155
+ if polynom < 2:
156
+
157
+ raise ValueError('"polynom" must be larger than 1')
158
+
159
+
160
+
161
+ if type(der) is not int:
162
+
163
+ raise TypeError('"der" must be an integer')
164
+
165
+
166
+
167
+ if der < 0:
168
+
169
+ raise TypeError('"der" must be an positive integer')
170
+
171
+
172
+
173
+ if der > 2:
174
+
175
+ raise TypeError('"der" must be less than 3')
176
+
177
+
178
+
179
+ half_window = window // 2
180
+
181
+ polynom += 1
182
+
183
+
184
+
185
+ # Initialize variables
186
+
187
+ A = np.empty((window, polynom)) # Matrix
188
+
189
+ tA = np.empty((polynom, window)) # Transposed matrix
190
+
191
+ t = np.empty(window) # Local x variables
192
+
193
+ y_smoothed = np.full(len(y), np.nan)
194
+
195
+
196
+
197
+ # Start smoothing
198
+
199
+ for i in range(half_window, len(x) - half_window, 1):
200
+
201
+ # Center a window of x values on x[i]
202
+
203
+ for j in range(0, window, 1):
204
+
205
+ t[j] = x[i + j - half_window] - x[i]
206
+
207
+
208
+
209
+ # Create the initial matrix A and its transposed form tA
210
+
211
+ for j in range(0, window, 1):
212
+
213
+ r = 1.0
214
+
215
+ for k in range(0, polynom, 1):
216
+
217
+ A[j, k] = r
218
+
219
+ tA[k, j] = r
220
+
221
+ r *= t[j]
222
+
223
+
224
+
225
+ # Multiply the two matrices
226
+
227
+ tAA = np.matmul(tA, A)
228
+
229
+
230
+
231
+ # Invert the product of the matrices
232
+
233
+ tAA = np.linalg.inv(tAA)
234
+
235
+
236
+
237
+ # Calculate the pseudoinverse of the design matrix
238
+
239
+ coeffs = np.matmul(tAA, tA)
240
+
241
+
242
+
243
+ # Calculate c0 which is also the y value for y[i]
244
+
245
+ y_smoothed[i] = 0
246
+
247
+ for j in range(0, window, 1):
248
+
249
+ #y_smoothed[i] += coeffs[0, j] * y[i + j - half_window]
250
+
251
+ y_smoothed[i] += coeffs[der, j] * y[i + j - half_window]
252
+
253
+
254
+
255
+ # If at the end or beginning, store all coefficients for the polynom
256
+
257
+ if i == half_window:
258
+
259
+ first_coeffs = np.zeros(polynom)
260
+
261
+ for j in range(0, window, 1):
262
+
263
+ for k in range(polynom):
264
+
265
+ first_coeffs[k] += coeffs[k, j] * y[j]
266
+
267
+ elif i == len(x) - half_window - 1:
268
+
269
+ last_coeffs = np.zeros(polynom)
270
+
271
+ for j in range(0, window, 1):
272
+
273
+ for k in range(polynom):
274
+
275
+ last_coeffs[k] += coeffs[k, j] * y[len(y) - window + j]
276
+
277
+
278
+
279
+ # Interpolate the result at the left border
280
+
281
+ for i in range(0, half_window, 1):
282
+
283
+ y_smoothed[i] = 0
284
+
285
+ x_i = 1
286
+
287
+ #for j in range(0, polynom, 1):
288
+
289
+ for j in range(der, polynom, 1):
290
+
291
+ y_smoothed[i] += first_coeffs[j] * x_i
292
+
293
+ x_i *= x[i] - x[half_window]
294
+
295
+
296
+
297
+ # Interpolate the result at the right border
298
+
299
+ for i in range(len(x) - half_window, len(x), 1):
300
+
301
+ y_smoothed[i] = 0
302
+
303
+ x_i = 1
304
+
305
+ #for j in range(0, polynom, 1):
306
+
307
+ for j in range(der, polynom, 1):
308
+
309
+ y_smoothed[i] += last_coeffs[j] * x_i
310
+
311
+ x_i *= x[i] - x[-half_window - 1]
312
+
313
+
314
+
315
+ return y_smoothed
316
+
317
+
318
+
319
+ if __name__ == '__main__':
320
+
321
+ import numpy as np
322
+
323
+ import matplotlib.pyplot as plt
324
+
325
+
326
+
327
+ # テスト用ダミーデータ
328
+
329
+ x = np.arange(0, 100)
330
+
331
+ y = np.arange(10, 110) + np.random.randn(100) * 2
332
+
333
+ y11 = non_uniform_savgol_der(x, y, 11, 2, 0)
334
+
335
+ plt.plot(x, y, x, y11)
336
+
337
+ plt.grid()
338
+
339
+ plt.show()
340
+
341
+
342
+
343
+ # 微分
344
+
345
+ dy = np.gradient(y, x)
346
+
347
+ dy11 = non_uniform_savgol_der(x, y, 11, 2, 1)
348
+
349
+ plt.plot(x, dy, x, dy11)
350
+
351
+ plt.grid()
352
+
353
+ plt.show()
354
+
355
+
356
+
357
+ # 二階微分
358
+
359
+ ddy = np.gradient(dy, x)
360
+
361
+ ddy11 = non_uniform_savgol_der(x, y, 11, 2, 2)
362
+
363
+ plt.plot(x, ddy, x, ddy11)
364
+
365
+ plt.grid()
366
+
367
+ plt.show()
368
+
369
+ ```

1

誤りの告知

2020/12/02 04:31

投稿

jbpb0
jbpb0

スコア7651

test CHANGED
@@ -35,3 +35,13 @@
35
35
  ```
36
36
 
37
37
  点数が多いほど平滑化が強くなります
38
+
39
+
40
+
41
+ 【訂正】
42
+
43
+ これはxが等間隔じゃないと使えない手法です
44
+
45
+ データ見たら等間隔ではないので、使えませんね
46
+
47
+ 失礼しました