【问题标题】:Convert a Pandas DataFrame to a multidimensional ndarray将 Pandas DataFrame 转换为多维 ndarray
【发布时间】:2018-05-22 18:07:18
【问题描述】:

我有一个 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.598s,Divikars解耗时7.405s,JohnE解耗时7.867s,Wens解耗时6.286s。

【问题讨论】:

标签: python pandas numpy


【解决方案1】:

另一种解决方案是使用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 的 DataArrays 包含标签,因此您可以继续使用该对象而不是拉出 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

【讨论】:

  • 谢谢,我还不知道这个包:-)
【解决方案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]]])

【讨论】:

  • 感谢您的回答,不幸的是,这对于 4 维都失败了,因为元素的顺序混淆了(我怀疑堆栈和取消堆栈的参数必须更改)
  • @DanielSch。是的,你需要改变它
【解决方案3】:

这是一种 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

【讨论】:

  • 不幸的是,如果输入不是整数,则会出现错误“TypeError:'numpy.float64' 对象不能被解释为整数”。添加“.astype(int)后,错误信息为“TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('int64') based on the rule 'same_kind'”。
  • @DanielSch。您需要将索引作为整数,这似乎不是错误的。所以,做:arr = df[['z','y','x']].values.astype(int).
  • 对不起,我忘了提到这一点:我的应用程序中的值是浮点数,因此不能将它们转换为整数:-/
  • @DanielSch。你说它不是一个选项是什么意思?转换的障碍是什么?它将仅提取和转换 X、Y、Z 列,而不是 Value col。输入数据框保持不变。
  • @Divikar 我想我必须做arr = np.round(df[['z','y','x']].values / resolution).astype(int),然后它才能工作。谢谢。
【解决方案4】:

您可以使用groupby,然后使用Transform Pandas DataFrame with n-level hierarchical index into n-D Numpy array 中的方法:

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]]]

【讨论】:

  • 作为额外奖励,您甚至可以在grouped.index.levels 中获得“轴标签”。
  • 我收到 AttributeError: 'MultiIndex' object has no attribute 'labels'。
猜你喜欢
  • 1970-01-01
  • 2019-10-31
  • 1970-01-01
  • 1970-01-01
  • 2022-07-22
  • 2017-03-17
  • 2017-03-23
  • 2017-04-13
  • 2021-03-29
相关资源
最近更新 更多