phép nội suy ngược xarray (trên tọa độ, không phải trên dữ liệu)


8

Tôi có một DataArray sau đây

arr = xr.DataArray([[0.33, 0.25],[0.55, 0.60],[0.85, 0.71],[0.92,0.85],[1.50,0.96],[2.5,1.1]],[('x',[0.25,0.5,0.75,1.0,1.25,1.5]),('y',[1,2])])

Điều này cho đầu ra sau

<xarray.DataArray (x: 6, y: 2)>
array([[0.33, 0.25],
       [0.55, 0.6 ],
       [0.85, 0.71],
       [0.92, 0.85],
       [1.5 , 0.96],
       [2.5 , 1.1 ]])
Coordinates:
  * x        (x) float64 0.25 0.5 0.75 1.0 1.25 1.5
  * y        (y) int32 1 2

hoặc sắp xếp bên dưới với x và đầu ra (z) cạnh nhau để thuận tiện.

x         z (y=1)   z(y=2)
0.25      0.33      0.25
0.50      0.55      0.60
0.75      0.85      0.71
1.00      0.92      0.85
1.25      1.50      0.96
1.50      2.50      1.10

Dữ liệu tôi có là kết quả của một số giá trị đầu vào. Một trong số đó là giá trị x. Có một số kích thước khác (như y) cho các giá trị đầu vào khác. Tôi muốn biết khi nào giá trị đầu ra (z) của tôi tăng lên hơn 1,00, giữ cho các kích thước khác cố định và thay đổi giá trị x. Trong ví dụ 2 chiều ở trên, tôi muốn nhận được câu trả lời [1.03 1.32]. Bởi vì giá trị 1,03 cho x sẽ cho tôi 1,00 cho z khi y = 1 và giá trị 1,32 cho x sẽ cho tôi 1,00 cho z khi y = 2.

chỉnh sửa: Vì đầu ra z sẽ tăng khi tăng x, nên chỉ có một điểm mà z sẽ có 1.0 là đầu ra.

Có cách nào hiệu quả để đạt được điều này với xarray không? Bảng thực tế của tôi lớn hơn nhiều và có 4 đầu vào (kích thước).

Cảm ơn bạn đã giúp đỡ!

Câu trả lời:


4

xarray có một chức năng rất tiện dụng cho việc này: xr.interpnó sẽ thực hiện phép nội suy tuyến tính từng phần của một xarray.

Trong trường hợp của bạn, bạn có thể sử dụng nó để có được phép nội suy từng phần của các điểm (x, y1) và (x, y1). Khi điều này được thực hiện, điều duy nhất còn lại phải làm là nhận giá trị của xmảng nội suy được liên kết với giá trị đóng của y1/y2/..mảng được nội suy của bạn với số mục tiêu (1,00 trong ví dụ của bạn).

Đây là cách nó có thể trông như thế nào:

y_dims = [0, 1,] 
target_value = 1.0
# create a 'high resolution` version of your data array:
arr_itp = arr.interp(x=np.linspace(arr.x.min(), arr.x.max(), 10000))
for y in y_dims:
    # get the index of closest data
    x_closest = np.abs(arr_itp.isel(y=y) - target_value).argmin()
    print(arr_itp.isel(y=y, x=x_closest))

>>> <xarray.DataArray ()>
>>> array(0.99993199)
>>> Coordinates:
>>>     y        int64 1
>>>     x        float64 1.034
>>> <xarray.DataArray ()>
>>> array(1.00003)
>>> Coordinates:
>>>     y        int64 2
>>>     x        float64 1.321


Trong khi điều này hoạt động, nó không phải là một cách thực sự hiệu quả để tiếp cận vấn đề và đây là 2 lý do tại sao không:

  1. Sử dụng xr.interp sẽ thực hiện phép nội suy từng phần của toàn bộ DataArray. Howerver, chúng tôi chỉ cần nội suy giữa hai điểm gần nhất với giá trị mục tiêu của bạn.
  2. Ở đây, phép nội suy là một đường thẳng nằm giữa 2 điểm. Nhưng nếu chúng ta biết một tọa độ của một điểm trên đường thẳng đó (y = 1,00) thì chúng ta có thể tính toán tọa độ khác bằng cách giải phương trình tuyến tính của đường thẳng và bài toán được giải trong một vài phép tính số học.

Khi tính đến những lý do này, chúng tôi có thể phát triển một giải pháp hiệu quả hơn cho vấn đề của bạn:

# solution of linear function between two points (2. reason)
def lin_itp(p1,p2,tv):
    """Get x coord of point on line

    Determine the x coord. of a point (x, target_value) on the line
    through the points p1, p2.

    Approach:
      - parametrize x, y between p1 and p2: 
          x = p1[0] + t*(p2[0]-p1[0])
          y = p1[1] + t*(p2[1]-p1[1])
      - set y = tv and resolve 2nd eqt for t
          t = (tv - p1[1]) / (p2[1] - p1[1])
      - replace t in 1st eqt with solution for t
          x = p1[0] + (tv - p1[1])*(p2[0] - p1[0])/(p2[1] - p1[1])
    """
    return float(p1[0] + (tv - p1[1])*(p2[0] - p1[0])/(p2[1] - p1[1])) 

# target value:
t_v = 1.0
for y in [0, 1]:
    arr_sd = arr.isel(y=y)
    # get index for the value closest to the target value (but smaller)
    s_udim = int(xr.where(arr_sd - t_v <=0, arr_sd, arr_sd.min()).argmax())
    # I'm explicitly defining the two points here
    ps_itp = arr_sd[s_udim:s_udim+2]
    p1, p2 = (ps_itp.x[0], ps_itp[0]), (ps_itp.x[1], ps_itp[1])
    print(lin_itp(p1,p2,t_v))

>>> 1.0344827586206897
>>> 1.3214285714285714


1
Bạn đã mắc một lỗi trong đó bạn nói: "Array_sd = Array.isel (y = 0)" bạn có nghĩa là "Array_sd = Array.isel (y = y)"
Hoogendijk

@Hoogendijk bạn nói đúng, cảm ơn. đã không nhìn thấy điều đó. Hy vọng câu trả lời là hữu ích. :)
jojo

vâng, nó rất hữu ích nhưng tôi vẫn quyết định xem liệu tôi có thể cải thiện nó hay không và cần phải có vòng lặp for.
Hoogendijk

0

Vấn đề tôi gặp phải với câu trả lời của jojo là rất khó để mở rộng nó theo nhiều chiều và để giữ cấu trúc xarray. Do đó, tôi quyết định xem xét thêm về điều này. Tôi đã sử dụng một số ý tưởng từ mã của jojo để đưa ra câu trả lời dưới đây.

Tôi tạo hai mảng, một với điều kiện là các giá trị nhỏ hơn giá trị tôi tìm và một với điều kiện chúng cần lớn hơn. Tôi thay đổi cái thứ hai theo hướng x bằng cách trừ đi 1. Bây giờ tôi kết hợp chúng trong một công thức nội suy tuyến tính bình thường. Hai mảng chỉ có các giá trị chồng lấp ở 'cạnh' của điều kiện. Nếu không được dịch chuyển bởi -1, sẽ không có giá trị nào trùng nhau. Trong dòng cuối cùng tôi tổng hợp theo hướng x và vì các giá trị khác là al NaN, tôi trích xuất giá trị chính xác và xóa hướng x khỏi DataArray trong quy trình.

def interpolate_dimension_x(arr, target_value, step):
    M0 = arr.where(arr - target_value <= 0)
    M1 = arr.where(arr - target_value > 0).shift(x=-1)

    work_mat = M0.x + step * (target_value - M0) / (M1 - M0)

    return work_mat.sum(dim='x')
interpolate_dimension_x(arr, 1, 0.25)

>>> <xarray.DataArray (y: 2)>
array([1.034483, 1.321429])
Coordinates:
  * y        (y) int32 1 2

Tôi có một số nhược điểm với mã của tôi. Mã chỉ hoạt động nếu M0 và M1 tìm thấy một giá trị đáp ứng điều kiện. Nếu không, tất cả các giá trị trong hàng đó sẽ được đặt thành NaN. Để ngăn sự cố với M0, tôi quyết định chỉ có giá trị x bắt đầu từ 0 vì giá trị mục tiêu của tôi luôn lớn hơn 0. Để ngăn sự cố với M1, tôi chọn giá trị x đủ lớn để tôi biết giá trị của mình nằm ở đó . Đương nhiên, đây không phải là giải pháp lý tưởng và có thể phá vỡ mã. Nếu tôi có thêm một chút kinh nghiệm với xarray và python tôi có thể viết lại. Tóm lại tôi có các mục sau đây tôi muốn giải quyết:

  • Làm thế nào để ngoại suy các giá trị ngoài phạm vi x? Tôi hiện chỉ đảm bảo phạm vi x của mình đủ lớn để các câu trả lời nằm trong phạm vi đó.
  • Làm thế nào để làm cho mã mạnh mẽ cho một bước thay đổi?
  • Cách tạo mã sao cho thứ nguyên của tôi có thể được chọn một cách linh hoạt (bây giờ nó chỉ hoạt động cho 'x')
  • Bất kỳ tối ưu hóa đều được đánh giá cao.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.