如何将第一个有效值向左移动?

时间:2019-06-05 14:38:06

标签: python pandas dataframe

我想将按日历年排序的数据框更改为过去的几年。

id   Y1   Y2      Y3     Y4  Y5
0   7.0  8.0     7.0    3.0  NaN
1   8.0  5.0     7.0    NaN  NaN
2   4.0  NaN     5.0    NaN  NaN
3   5.0  4.0     3.0    9.0  NaN
4   1.0  NaN     NaN    NaN  NaN
5   4.0  3.0     NaN    4.0  5.0

我需要将所有有效值都移到第一列。

预期结果应如下所示:

print(type(vector[0][0]))

注意:“ id”是一列,而不是索引。

4 个答案:

答案 0 :(得分:5)

您可以使用ogrid和索引对基础的numpy数组进行操作。

u = df.set_index('id').values
x, y = u.shape
r, c = np.ogrid[:x, :y]
m = (~np.isnan(u)).argmax(1)
cix = c + m[:, None]
cix[cix >= y] -= y
u[r, cix]

array([[ 7.,  8.,  7.,  3., nan],
       [ 8.,  5.,  7., nan, nan],
       [ 4., nan,  5., nan, nan],
       [ 5.,  4.,  3.,  9., nan],
       [ 1., nan, nan, nan, nan],
       [ 4.,  3., nan,  4.,  5.]])

答案 1 :(得分:4)

让我们尝试np.roll的逐行应用程序:

df = df.set_index('id')
# using @user3483203's argmax trick to optimize
df.apply(lambda x: np.roll(x, -(~np.isnan(x)).argmax()), axis=1, raw=True)

    2015  2016  2017  2018  2019
id                              
0    7.0   8.0   7.0   3.0   NaN
1    8.0   5.0   7.0   NaN   NaN
2    4.0   NaN   5.0   NaN   NaN
3    5.0   4.0   3.0   9.0   NaN
4    1.0   NaN   NaN   NaN   NaN
5    4.0   3.0   NaN   4.0   5.0

不幸的是,np.roll未被向量化以对单独的行执行不同的移位,并且为了保留值之间的NaN,必须这样做。

答案 2 :(得分:3)

找到每一行的班次,然后按组应用班次:

df = df.set_index('id')

# Required shift for each row
n = df.isnull()
s = (n*n.cumprod(1)).sum(1)*-1

for shift, idx in s.groupby(s).groups.items():
    df.loc[idx, :] = df.loc[idx].shift(shift, axis=1)

df.columns = [f'Y{i+1}' for i in range(df.shape[1])]
df = df.reset_index()

输出:

   id   Y1   Y2   Y3   Y4   Y5
0   0  7.0  8.0  7.0  3.0  NaN
1   1  8.0  5.0  7.0  NaN  NaN
2   2  4.0  NaN  5.0  NaN  NaN
3   3  5.0  4.0  3.0  9.0  NaN
4   4  1.0  NaN  NaN  NaN  NaN
5   5  4.0  3.0  NaN  4.0  5.0

答案 3 :(得分:3)

如何使用first_valid_index

df = df.set_index('id')
df.apply(lambda x: x.shift(-x.index.get_loc(x.first_valid_index())), axis=1)

输出:

    2015  2016  2017  2018  2019
id                              
0    7.0   8.0   7.0   3.0   NaN
1    8.0   5.0   7.0   NaN   NaN
2    4.0   NaN   5.0   NaN   NaN
3    5.0   4.0   3.0   9.0   NaN
4    1.0   NaN   NaN   NaN   NaN
5    4.0   3.0   NaN   4.0   5.0

详细信息

首先,apply使用axis=1,这将在每行上应用一个函数。

使用first_valid_index查找pd.Series中第一个非空值的索引。请记住,在这种情况下,pd.Series是数据帧的每一行。

接下来,使用get_loc确定该索引的整数位置。

最后,使用shift将pd.Series的值向后移动pd.Series索引中的整数位置。


而且,您可以添加第二行以重命名列

df.apply(lambda x: x.shift(-x.index.get_loc(x.first_valid_index())), axis=1)\
  .set_axis(['Y'+str(i) for i in range(1, df.shape[1]+1)], axis=1, inplace=False)

输出:

     Y1   Y2   Y3   Y4   Y5
id                         
0   7.0  8.0  7.0  3.0  NaN
1   8.0  5.0  7.0  NaN  NaN
2   4.0  NaN  5.0  NaN  NaN
3   5.0  4.0  3.0  9.0  NaN
4   1.0  NaN  NaN  NaN  NaN
5   4.0  3.0  NaN  4.0  5.0