更有效地从包含随机放置的NaN的列表中提取不间断的数字序列

时间:2018-05-17 23:04:09

标签: python pandas numpy dictionary nan

我最近将一个庞大的excel文件转换为pandas数据帧,该文件中填充了随机列的空单元格。结果数据帧因此有很长的NaN段。

然而,在对数据帧进行一些操作之后,我在这里和那里创建了一些较小的NaN串,以及我想保留的那些NaN。所以我试着编写一个函数来创建一个由 足够大量的NaN 分隔的数字块的字典(所以输出只能由原始Excel文件缺少数据)。

我的代码:

def nan_stripper(data,bound):

        newdict = {}
        chunk = 0

        i = 0
        while i < len(data):

            if ~np.isnan(data[i]):
                newdict.setdefault('chunk ' + str(chunk),[]).append(data[i])                
                i += 1
                continue

            elif np.isnan(data[i]):

                # Create clear buffer for next chunk of nan's
                buffer = []
                while np.isnan(data[i]):
                    buffer.append(data[i])
                    i += 1 

                    # When stretch ends, append processed nan's if below selected bound,
                    # and prepare for next number segment.
                    if ~np.isnan(data[i]):
                        if len(buffer) < bound + 1:
                            newdict['chunk ' + str(chunk)].extend(buffer)

                        if len(buffer) >= bound + 1:
                            chunk += 1  

        return newdict

在这里进行测试,使用NaN 3的界限:

a = np.array([-1,1,2,3,np.nan,np.nan,np.nan,np.nan,4,5,np.nan,np.nan,7,8,9,10])
b = nan_stripper(a,3)

print(b)
{'chunk 0': [-1.0, 1.0, 2.0, 3.0],
 'chunk 1': [4.0, 5.0, nan, nan, 7.0, 8.0, 9.0, 10.0]}

事情是,我不相信我的代码是有效的,因为我使用了一个奇怪的字典方法( found here ),以便将附加值附加到单个键。是否有任何我想要的简单优化,或者你们中的一些人是否已经采用了完全不同的方式?我觉得这不可能是最狡猾的方式。

提前谢谢。

与答案的比较: 在确定了我原来的方法和Paul Panzer的时间之后,这些是对那些感兴趣的人的结果。

enter image description here

1 个答案:

答案 0 :(得分:1)

这是一个矢量化版本。它会滑动一个大小限制在数据上的窗口,保持当前的nan计数,​​并标记计数等于绑定的偏移量(即窗口仅包含nans的位置)。

然后,它将这些标记的一部分混合在一起,在边界处分裂并丢弃其他所有标记(仅包含纳米标记)。

import numpy as np

def nan_split(data, bound, make_dict=False):
    data = np.asanyarray(data)
    # find nans, convert boolean mask to int8 to enable basic arithmetic
    m = np.isnan(data).view(np.int8)
    # compute windowed sum (same as windowed nan count)
    m[bound:] -= m[:-bound]
    # find all all-nan window offsets
    m = m.cumsum() == bound
    # find offsets where it switches between all-nan and non all-nan
    idx, = np.where(m[1:] != m[:-1])
    # correct for window size and edge loss
    idx[::2] += 2-bound
    idx[1::2] += 1
    # split
    if make_dict:
        return {f'chunk {i}': c
                for i, c in enumerate(np.split(data, idx)[2*(idx[0]==0)::2])}
    else:
        return np.split(data, idx)[2*(idx[0]==0)::2]