繁体   English   中英

将Pandas DataFrame转换为多维ndarray

[英]Convert a Pandas DataFrame to a multidimensional ndarray

我有一个DataFrame,其中包含x,y,z坐标和此位置的值,我希望将其转换为3维ndarray。

为了使事情变得更复杂,DataFrame中并不存在所有值(这些值可以在ndarray中替换为NaN)。

只是一个简单的例子:

df = pd.DataFrame({'x': [1, 2, 1, 3, 1, 2, 3, 1, 2], 
                   'y': [1, 1, 2, 2, 1, 1, 1, 2, 2],
                   'z': [1, 1, 1, 1, 2, 2, 2, 2, 2],
                   'value': [1, 2, 3, 4, 5, 6, 7, 8, 9]})

应该导致ndarray:

array([[[  1.,   2.,  nan],
        [  3.,  nan,   4.]],

       [[  5.,   6.,   7.],
        [  8.,   9.,  nan]]])

对于两个维度,这很容易:

array = df.pivot_table(index="y", columns="x", values="value").as_matrix()

但是,此方法不能应用于三维或更多维度。

你能给我一些建议吗?

奖励点如果这也适用于三个以上的维度,处理多个定义的值(通过取平均值)并确保所有x,y,z坐标是连续的(通过在缺少坐标时插入NaN的行/列)。

编辑:更多解释:

我从CSV文件中读取数据,该文件包含x,y,z坐标列,可选地包括此点和频率的频率和测量值。 然后我将坐标舍入到指定的精度(例如0.1m),并希望获得一个ndarray,其中包含每个(圆形)坐标处的平均测量值。 值的指示不需要与位置一致。 但是他们需要按正确的顺序排列。

编辑:我刚刚进行了快速的性能测试:

jakevdp的解决方案需要1.598秒,Divikars解决方案需要7.405秒,JohnE的解决方案需要7.867秒,Wens解决方案需要6.286秒才能完成。

您可以使用groupby然后使用带有n级层次索引的Transform Pandas DataFrame进入nD Numpy数组

grouped = df.groupby(['z', 'y', 'x'])['value'].mean()

# create an empty array of NaN of the right dimensions
shape = tuple(map(len, grouped.index.levels))
arr = np.full(shape, np.nan)

# fill it using Numpy's advanced indexing
arr[grouped.index.labels] = grouped.values.flat

print(arr)
# [[[  1.   2.  nan]
#   [  3.  nan   4.]]
# 
#  [[  5.   6.   7.]
#   [  8.   9.  nan]]]

这是一种NumPy方法 -

def dataframe_to_array_averaged(df):
    arr = df[['z','y','x']].values
    arr -= arr.min(0)
    out_shp = arr.max(0)+1

    L = np.prod(out_shp)

    val = df['value'].values
    ids = np.ravel_multi_index(arr.T, out_shp)

    avgs = np.bincount(ids, val, minlength=L)/np.bincount(ids, minlength=L)
    return avgs.reshape(out_shp)

请注意,这显示了一个警告,因为对于没有x,y,z三元组的地方将没有计数,因此平均值将是0/0 = NaN ,但由于这是这些地方的预期输出,您可以忽略警告那里。 为避免此警告,我们可以使用索引,如第二种方法(替代方法)中所述。

样品运行 -

In [106]: df
Out[106]: 
   value  x  y  z
0      1  1  1  1  # <=== this is repeated
1      2  2  1  1
2      3  1  2  1
3      4  3  2  1
4      5  1  1  2
5      6  2  1  2
6      7  3  1  2
7      8  1  2  2
8      9  2  2  2
9      4  1  1  1  # <=== this is repeated

In [107]: dataframe_to_array_averaged(df)
__main__:42: RuntimeWarning: invalid value encountered in divide
Out[107]: 
array([[[ 2.5,  2. ,  nan],
        [ 3. ,  nan,  4. ]],

       [[ 5. ,  6. ,  7. ],
        [ 8. ,  9. ,  nan]]])

替代方法

为了避免警告,另一种方式是这样的 -

out = np.full(out_shp,  np.nan)
sums = np.bincount(ids, val)
unq_ids, count = np.unique(ids, return_counts=1)
out.flat[:unq_ids[-1]] = sums
out.flat[unq_ids] /= count

另一个解决方案是使用xarray包:

import pandas as pd
import xarray as xr
df = pd.DataFrame({'x': [1, 2, 1, 3, 1, 2, 3, 1, 2], 
                   'y': [1, 1, 2, 2, 1, 1, 1, 2, 2],
                   'z': [1, 1, 1, 1, 2, 2, 2, 2, 2],
                   'value': [1, 2, 3, 4, 5, 6, 7, 8, 9]})
df = pd.pivot_table(df, values='value', index=['x', 'y', 'z'])
xrTensor = xr.DataArray(df).unstack("dim_0")
array = xrTensor.values[0].T
print(array)

输出:

array([[[ 1.,  2., nan],
        [ 3., nan,  4.]],

       [[ 5.,  6.,  7.],
        [ 8.,  9., nan]]])

请注意, xrTensor对象非常方便,因为xarray的DataArray包含标签,因此您可以继续使用该对象而不是拉出ndarray

print(xrTensor)

输出:

<xarray.DataArray (dim_1: 1, x: 3, y: 2, z: 2)>
array([[[[ 1.,  5.],
         [ 3.,  8.]],

        [[ 2.,  6.],
         [nan,  9.]],

        [[nan,  7.],
         [ 4., nan]]]])
Coordinates:
  * dim_1    (dim_1) object 'value'
  * x        (x) int64 1 2 3
  * y        (y) int64 1 2
  * z        (z) int64 1 2

我们可以使用stack

np.reshape(df.groupby(['z', 'y', 'x'])['value'].mean().unstack([1,2]).stack([0,1],dropna=False).values,(2,2,3))


Out[451]: 
array([[[  1.,   2.,  nan],
        [  3.,  nan,   4.]],
       [[  5.,   6.,   7.],
        [  8.,   9.,  nan]]])

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM