对于数据帧的每一列,熊猫掉落在第一个有效索引之前和最后一个有效索引之后

时间:2018-06-22 08:19:44

标签: python pandas

我有一个这样的数据框:

df = pd.DataFrame({'timestamp':pd.date_range('2018-01-01', '2018-01-02', freq='2h', closed='right'),'col1':[np.nan, np.nan, np.nan, 1,2,3,4,5,6,7,8,np.nan], 'col2':[np.nan, np.nan, 0, 1,2,3,4,5,np.nan,np.nan,np.nan,np.nan], 'col3':[np.nan, -1, 0, 1,2,3,4,5,6,7,8,9], 'col4':[-2, -1, 0, 1,2,3,4,np.nan,np.nan,np.nan,np.nan,np.nan]
              })[['timestamp', 'col1', 'col2', 'col3', 'col4']]

如下所示:

             timestamp  col1  col2  col3  col4
0  2018-01-01 02:00:00   NaN   NaN   NaN  -2.0
1  2018-01-01 04:00:00   NaN   NaN  -1.0  -1.0
2  2018-01-01 06:00:00   NaN   0.0   NaN   0.0
3  2018-01-01 08:00:00   1.0   1.0   1.0   1.0
4  2018-01-01 10:00:00   2.0   NaN   2.0   2.0
5  2018-01-01 12:00:00   3.0   3.0   NaN   3.0
6  2018-01-01 14:00:00   NaN   4.0   4.0   4.0
7  2018-01-01 16:00:00   5.0   NaN   5.0   NaN
8  2018-01-01 18:00:00   6.0   NaN   6.0   NaN
9  2018-01-01 20:00:00   7.0   NaN   7.0   NaN
10 2018-01-01 22:00:00   8.0   NaN   8.0   NaN
11 2018-01-02 00:00:00   NaN   NaN   9.0   NaN

现在,我想找到一种有效且有效的方法来删除第一个有效索引之前和最后一个有效索引之后(每列!不计算时间戳)。在此示例中,我有4列,但实际上我有更多列,大约600列。我正在寻找一种将第一个有效索引之前的所有NaN值和最后一个有效索引之后的所有NaN值进行斩波的方法。

我想一种方法是循环遍历。但是还有更好的方法吗?这种方式必须高效。我试图使用melt“取消透视图”数据框,但这无济于事。

显而易见的是,斩波后每一列的行数都会不同。因此,我希望结果是一个带有时间戳和相关列的数据帧列表(每列一个)。例如:

             timestamp  col1   
3  2018-01-01 08:00:00   1.0  
4  2018-01-01 10:00:00   2.0   
5  2018-01-01 12:00:00   3.0   
6  2018-01-01 14:00:00   NaN   
7  2018-01-01 16:00:00   5.0   
8  2018-01-01 18:00:00   6.0   
9  2018-01-01 20:00:00   7.0   
10 2018-01-01 22:00:00   8.0    

我的尝试

我尝试过这样:

final = []
columns = [c for c in df if c !='timestamp']
for col in columns:
    first = df.loc[:, col].first_valid_index()
    last = df.loc[:, col].last_valid_index()
    final.append(df.loc[:, ['timestamp', col]].iloc[first:last+1, :])

3 个答案:

答案 0 :(得分:1)

一个想法是在将索引设置为timestamp后使用列表或字典理解。您应该对数据进行测试,以查看这是否可以解决性能问题。如果您的局限性是记忆力,这不太可能帮助您。

df = df.set_index('timestamp')

final = {col: df[col].loc[df[col].first_valid_index(): df[col].last_valid_index()] \
         for col in df}

print(final)

{'col1': timestamp
2018-01-01 08:00:00    1.0
2018-01-01 10:00:00    2.0
2018-01-01 12:00:00    3.0
2018-01-01 14:00:00    4.0
2018-01-01 16:00:00    5.0
2018-01-01 18:00:00    6.0
2018-01-01 20:00:00    7.0
2018-01-01 22:00:00    8.0
Name: col1, dtype: float64,
...
'col4': timestamp
2018-01-01 02:00:00   -2.0
2018-01-01 04:00:00   -1.0
2018-01-01 06:00:00    0.0
2018-01-01 08:00:00    1.0
2018-01-01 10:00:00    2.0
2018-01-01 12:00:00    3.0
2018-01-01 14:00:00    4.0
Name: col4, dtype: float64}

答案 1 :(得分:1)

您可以使用函数式编程的功能,并将函数应用于每一列。这可能会加快速度。另外,当timestamps看起来已排序时,您可以将它们用作Datarame的索引。

df.set_index('timestamp', inplace=True)

final = []
def func(col):
    first = col.first_valid_index()
    last = col.last_valid_index()
    final.append(col.loc[first:last])
    return

df.apply(func)

此外,您还可以在一个衬纸中压实所有东西:

final = []
df.apply(lambda col: final.append(col.loc[col.first_valid_index() : col.last_valid_index()]))

答案 2 :(得分:1)

我的方法是为每一列及其倒数找到NaN的累积和,并过滤那些大于0的条目。然后,我执行dict理解以返回每一列的数据框(如果您愿意,可以将其更改为列表)。

以您的示例为例

cols = [c for c in df.columns if c!='timestamp']

result_dict = {c: df[(df[c].notnull().cumsum() > 0) &
                     (df.ix[::-1,c].notnull().cumsum()[::-1] > 0)][['timestamp', c]]
               for c in cols}