【问题标题】:How to create list of equally-shaped 2d matrices from not equally-shaped 2d arrays (by filling with NaNs)?如何从不同形状的二维数组(通过填充 NaN)创建形状相同的二维矩阵列表?
【发布时间】:2020-08-27 18:07:22
【问题描述】:

听起来与上一个相似,但问题不同:

我可以通过执行以下操作从 df 创建“增量增长”样本:

# df = { take an average float dataframe of 0.5-1mio rows & 20-50 cols ...}

arr    = np.asarray(df)
res    = list((map(lambda i: arr[:i], range(1,df.shape[0]+1))))

print(res)
>>>[  
    [                                                                                
    ["2019-06-17 08:45:00",     12089.89,     12089.89,    12087.71,      12087.71  ]  
                                                                                       ],
  [
   ["2019-06-17 08:45:00",     12089.89,     12089.89,   12087.71,      12087.71   ],  
   ["2019-06-17 08:46:00",     12087.91,      np.nan,    12087.71,      12087.91   ]  
                                                                                        ], 
  [
   ["2019-06-17 08:45:00",     12089.89,     12089.89,    12087.71,      12087.71   ], 
   ["2019-06-17 08:46:00" ,    12087.91  ,     np.nan,    12087.71,      12087.91   ],   
   ["2019-06-17 08:47:00" ,    12088.21  ,   12088.21,    12084.21   ,   12085.21   ]   
                                                                                        ], 
  [
   ["2019-06-17 08:45:00",     12089.89,     12089.89 ,   12087.71 ,     12087.71   ],    
   ["2019-06-17 08:46:00" ,    12087.91 ,     np.nan,     12087.71,      12087.91   ], 
   ["2019-06-17 08:47:00" ,    12088.21 ,    12088.21  ,  12084.21  ,    12085.21   ],    
   ["2019-06-17 08:48:00" ,    12085.09 ,    12090.21  ,  12084.91  ,    12089.41   ] 
                                                                                        ], 
  [
   ["2019-06-17 08:45:00",     12089.89,     12089.89 ,   12087.71  ,    12087.71    ],    
   ["2019-06-17 08:46:00" ,    12087.91 ,    np.nan,      12087.71,      12087.91    ], 
   ["2019-06-17 08:47:00" ,    12088.21 ,    12088.21  ,  12084.21   ,   12085.21    ],   
   ["2019-06-17 08:48:00" ,    12085.09 ,    12090.21  ,  12084.91   ,   12089.41    ],  
   ["2019-06-17 08:49:00" ,    12089.71 ,    12090.21  ,  12087.21   ,   12088.21    ]   
                                                                                        ]
                                                                                                 ]

但它们的形状不同(故意)。 所以我想用 np.nan-rows 填充它们。

重要提示:np.nan-rows 可以位于样本中的任何位置,只要它们不破坏原始行即可。 -> 所以它们可以随机位于行之间,但不能更改原始行。

TL,DR:我需要保持原始数据行的顺序,而不是更改这些行中的值,否则用 np.nan-rows 填充样本,直到它们都具有相同的长度(->最长),无论在哪里。 (并且以一种省时的方式,如何?)

理想结果如下所示:(您可以在下面看到随机 np.nanrow 定位的另一种可能结果)。

print(new_res)
>>>
[  
  [
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],            
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],                                                                    
   ["2019-06-17 08:45:00",     12089.89,     12089.89,    12087.71,      12087.71  ]  
                                                                                       ],
  [
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   ["2019-06-17 08:45:00",     12089.89,     12089.89,   12087.71,      12087.71   ],  
   ["2019-06-17 08:46:00",     12087.91,      np.nan,    12087.71,      12087.91   ]  
                                                                                        ], 
  [ 
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   ["2019-06-17 08:45:00",     12089.89,     12089.89,    12087.71,      12087.71   ], 
   ["2019-06-17 08:46:00" ,    12087.91  ,     np.nan,    12087.71,      12087.91   ],   
   ["2019-06-17 08:47:00" ,    12088.21  ,   12088.21,    12084.21   ,   12085.21   ]   
                                                                                        ], 
  [
   [                np.nan        np.nan        np.nan       np.nan        np.nan  ]  
   ["2019-06-17 08:45:00",     12089.89,     12089.89,    12087.71 ,     12087.71   ],    
   ["2019-06-17 08:46:00" ,    12087.91 ,     np.nan,     12087.71,      12087.91   ], 
   ["2019-06-17 08:47:00" ,    12088.21 ,    12088.21,    12084.21  ,    12085.21   ],    
   ["2019-06-17 08:48:00" ,    12085.09 ,    12090.21,    12084.91  ,    12089.41   ] 
                                                                                        ], 
  [
   ["2019-06-17 08:45:00",     12089.89,     12089.89 ,   12087.71  ,    12087.71    ],    
   ["2019-06-17 08:46:00" ,    12087.91 ,    np.nan,      12087.71,      12087.91    ], 
   ["2019-06-17 08:47:00" ,    12088.21 ,    12088.21  ,  12084.21   ,   12085.21    ],   
   ["2019-06-17 08:48:00" ,    12085.09 ,    12090.21  ,  12084.91   ,   12089.41    ],  
   ["2019-06-17 08:49:00" ,    12089.71 ,    12090.21  ,  12087.21   ,   12088.21    ]   
                                                                                        ]
                                                                                                 ]

随机添加的 np.nan-rows 样本:

print(new_res)
>>>
[  
  [
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],                                                                      
   ["2019-06-17 08:45:00",     12089.89,     12089.89,    12087.71,      12087.71  ] 
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ] 
                                                                                       ],
  [
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   ["2019-06-17 08:45:00",     12089.89,     12089.89,   12087.71,      12087.71   ],   
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   ["2019-06-17 08:46:00",     12087.91,      np.nan,    12087.71,      12087.91   ]  
                                                                                        ], 
  [  
   ["2019-06-17 08:45:00",     12089.89,     12089.89,    12087.71,      12087.71   ], 
   ["2019-06-17 08:46:00" ,    12087.91  ,     np.nan,    12087.71,      12087.91   ],   
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   ["2019-06-17 08:47:00" ,    12088.21  ,   12088.21,    12084.21   ,   12085.21   ]   
                                                                                        ], 
  [ 
   ["2019-06-17 08:45:00",     12089.89,     12089.89,    12087.71 ,     12087.71   ],    
   ["2019-06-17 08:46:00" ,    12087.91 ,     np.nan,     12087.71,      12087.91   ],  
   [                np.nan,       np.nan,       np.nan,      np.nan,       np.nan  ],  
   ["2019-06-17 08:47:00" ,    12088.21 ,    12088.21,    12084.21  ,    12085.21   ],    
   ["2019-06-17 08:48:00" ,    12085.09 ,    12090.21,    12084.91  ,    12089.41   ] 
                                                                                        ], 
  [
   ["2019-06-17 08:45:00",     12089.89,     12089.89 ,   12087.71  ,    12087.71    ],    
   ["2019-06-17 08:46:00" ,    12087.91 ,    np.nan,      12087.71,      12087.91    ], 
   ["2019-06-17 08:47:00" ,    12088.21 ,    12088.21  ,  12084.21   ,   12085.21    ],   
   ["2019-06-17 08:48:00" ,    12085.09 ,    12090.21  ,  12084.91   ,   12089.41    ],  
   ["2019-06-17 08:49:00" ,    12089.71 ,    12090.21  ,  12087.21   ,   12088.21    ]   
                                                                                        ]
                                                                                                 ]

【问题讨论】:

  • This 很有用。专注于“最小”。
  • @zabop 感谢您的提示^^ 编辑它。

标签: python pandas performance numpy dataframe


【解决方案1】:

我认为这可能对你有用:

arr = np.array(df)

n = arr.shape[0]

ind1, ind2 = np.tril_indices(n)

result = np.full((n, n, arr.shape[1]), np.nan, dtype=object)
result[ind1,ind2,:] = arr[ind2,:]

这给出了:

result = 
[[['2019-06-17 08:45:00' 12089.89 12089.89 12087.71 12087.71]
  [nan nan nan nan nan]
  [nan nan nan nan nan]
  [nan nan nan nan nan]
  [nan nan nan nan nan]]

 [['2019-06-17 08:45:00' 12089.89 12089.89 12087.71 12087.71]
  ['2019-06-17 08:46:00' 12087.91 nan      12087.71 12087.91]
  [nan nan nan nan nan]
  [nan nan nan nan nan]
  [nan nan nan nan nan]]

 [['2019-06-17 08:45:00' 12089.89 12089.89 12087.71 12087.71]
  ['2019-06-17 08:46:00' 12087.91 nan      12087.71 12087.91]
  ['2019-06-17 08:47:00' 12088.21 12088.21 12084.21 12085.21]
  [nan nan nan nan nan]
  [nan nan nan nan nan]]

 [['2019-06-17 08:45:00' 12089.89 12089.89 12087.71 12087.71]
  ['2019-06-17 08:46:00' 12087.91 nan      12087.71 12087.91]
  ['2019-06-17 08:47:00' 12088.21 12088.21 12084.21 12085.21]
  ['2019-06-17 08:48:00' 12085.09 12090.21 12084.91 12089.41]
  [nan nan nan nan nan]]

 [['2019-06-17 08:45:00' 12089.89 12089.89 12087.71 12087.71]
  ['2019-06-17 08:46:00' 12087.91 nan      12087.71 12087.91]
  ['2019-06-17 08:47:00' 12088.21 12088.21 12084.21 12085.21]
  ['2019-06-17 08:48:00' 12085.09 12090.21 12084.91 12089.41]
  ['2019-06-17 08:49:00' 12089.71 12090.21 12087.21 12088.21]]]

它都是基于 numpy 的,所以它应该非常有效,但我还没有测试过这种说法。结果也是一个维度为 3 的 numpy 数组,而不是矩阵列表,但是 numpy → list 很容易。

如果您想让您的 nan 的数据“高于”数据,如您的示例所示,您可以使用 result[ind1,n-ind1-1+ind2,:] = arr[ind2,:] 而不是result[ind1,ind2,:] = arr[ind2,:]

编辑:一些性能调整

第一个明显的优化是使用原生 numpy 类型,去掉第一列:

arr = np.array(df.loc[:,1:])

将之前的实现重写为函数:

def process_the_data(arr):
    n = arr.shape[0]
    ind1, ind2 = np.tril_indices(n)
    result = np.full((n, n, arr.shape[1]), np.nan, dtype=arr.dtype)
    result[ind1,ind2,:] = arr[ind2,:]
    return result

产生约 2 倍的速度提升

为了使用numba并获得更快的转换,最好使用显式for循环重写函数。如果数据集足够大,使用parallel=True(注意在外循环中使用 prange)会带来小的性能提升,但对于小数据集来说会更慢

from numba import njit, prange

@njit(parallel=True)
def process_the_data_jit(arr):
    n, m = arr.shape
    result = np.empty((n, n, m), dtype=arr.dtype)
    
    for i in prange(n):
        for j in range(i+1):
            for k in range(m):
                result[i,j,k] = arr[j,k]
        result[i,i+1:,:] = np.nan

    return result

这里的工作是受内存限制的,所以如果您不需要完整的 64 位精度,使用 float32 将加快速度约 1.8 倍。

总之,一个包含 2500 行的 df:

arr_original = np.array(df)
arr = np.array(df.loc[:,1:])

process_the_data(arr_original) #the previous result
process_the_data(arr)
process_the_data_jit(arr)
process_the_data_jit(arr.astype(np.float32))

分别拍摄(在我的机器上)

1.4 s
660 ms
80 ms
50 ms

因此,根据 dtype 的不同,这是一个不错的 17 倍或 28 倍加速

【讨论】:

  • 有没有办法用 numba 加快速度? (对不起,我需要从所有压力中恢复过来的迟到的答案)
  • 也许吧,但我不知道 numba 对“对象”类型的数组的处理效果如何。如果您真的需要速度,我建议您将日期+时间转换为数值并使用 np.float64 类型的 ndarray,或者将数组分成 2 部分,一部分包含日期,另一部分包含数值
  • 时间值只是为了可视化它。对不起,如果我造成误解,它们不会在数组中使用。 -> 因此我们可以使用float64 我试过@njit -> 但很遗憾我得到了编译错误。 :(你能试试吗?
  • 编辑添加了一些性能优化,可实现约 20 倍的加速
  • 我必须告诉你,我觉得应该避免整个计算,一半的时间花在编写 nans 上,剩下的就是一遍又一遍地复制相同的数据,所以除非你真的需要要将所有这些同时保存在内存中,我强烈建议您查看问题的替代表述
猜你喜欢
  • 2012-07-04
  • 2020-12-04
  • 1970-01-01
  • 2018-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多