大熊猫在每个指数处强制NaN到每列的底部

时间:2015-11-04 19:41:20

标签: python pandas

我有一个DataFrame,其中多行跨越每个索引。例如,第一个索引具有这样的结构:

df = pd.DataFrame([["A", "first", 1.0, 1.0, np.NaN],
            [np.NaN, np.NaN, 2.0, np.NaN, 2.0],
            [np.NaN, np.NaN, np.NaN, 3.0, 3.0]],
            columns=["ID", "Name", "val1", "val2", "val3"],
            index=[0, 0, 0])

Out[4]:
    ID   Name  val1  val2  val3
0    A  first     1     1   NaN
0  NaN    NaN     2   NaN     2
0  NaN    NaN   NaN     3     3

我想对每一列进行排序/排序,使得NaN位于该给定索引的每列的底部 - 结果如下所示:

    ID   Name  val1  val2  val3
0    A  first     1     1     2
0  NaN    NaN     2     3     3
0  NaN    NaN   NaN     NaN   NaN

更明确的示例可能如下所示:

df = pd.DataFrame([["A", "first", 1.0, 1.0, np.NaN],
            [np.NaN, np.NaN, 2.0, np.NaN, 2.0],
            [np.NaN, np.NaN, np.NaN, 3.0, 3.0],
            ["B", "second", 4.0, 4.0, np.NaN],
            [np.NaN, np.NaN, 5.0, np.NaN, 5.0],
            [np.NaN, np.NaN, np.NaN, 6.0, 6.0]],
            columns=[ "ID", "Name", "val1", "val2", "val3"],
            index=[0, 0, 0, 1, 1, 1])

Out[5]:
    ID    Name  val1  val2  val3
0    A   first     1     1   NaN
0  NaN     NaN     2   NaN     2
0  NaN     NaN   NaN     3     3
1    B  second     4     4   NaN
1  NaN     NaN     5   NaN     5
1  NaN     NaN   NaN     6     6

所需的结果如下所示:

    ID    Name  val1  val2  val3
0    A   first     1     1     2
0  NaN     NaN     2     3     3
0  NaN     NaN   NaN   NaN   NaN
1    B  second     4     4     5
1  NaN     NaN     5     6     6
1  NaN     NaN   NaN   NaN   NaN

我在这个数据框中有数千行,每个索引最多包含几百行。当我to_csv数据框时,我想要的结果非常有用。

我试图在整个数据框中使用sort_values(['val1','val2','val3']),但这会导致索引变得混乱。我试图迭代每个索引并进行排序,但这也不会将NaN限制在每个索引列的底部。我也尝试fillna到另一个值,例如0,但我在这里也没有成功。

虽然我确实错误地使用了它,na_position中的sort_values参数并没有产生预期的结果,但似乎这可能是想要的。

编辑:

最终df的索引不需要像我的第二个例子那样按数字顺序排列。

在@ Leb的第三个代码块的单行中将ignore_index更改为False

pd.concat([df[col].sort_values().reset_index(drop=True) for col in df], axis=1, ignore_index=True)

pd.concat([df[col].sort_values().reset_index(drop=True) for col in df], axis=1, ignore_index=False)

并且通过为给定索引中的所有行创建临时df,我能够完成这项工作 - 不是很漂亮,但它会按照我需要的方式来命令。如果某人(肯定)有更好的方法,请告诉我。

new_df = df.ix[0]
new_df = pd.concat([new_df[col].sort_values().reset_index(drop=True) for col in new_df], axis=1, ignore_index=False)
max_index = df.index[-1]
for i in range(1, max_index + 1):
    tmp = df.ix[i]
    tmp = pd.concat([tmp[col].sort_values().reset_index(drop=True) for col in tmp], axis=1, ignore_index=False)
    new_df = pd.concat([new_df,tmp])


In [10]: new_df
Out[10]:
    ID    Name  val1  val2  val3
0    A   first     1     1     2
1  NaN     NaN     2     3     3
2  NaN     NaN   NaN   NaN   NaN
0    B  second     4     4     5
1  NaN     NaN     5     6     6
2  NaN     NaN   NaN   NaN   NaN

3 个答案:

答案 0 :(得分:4)

我知道在github上讨论了将nans推到边缘的问题。对于你的特定框架,我可能会在Python级别手动完成,而不用担心性能太高。像

这样的东西
>>> df.groupby(level=0, sort=False).transform(lambda x: sorted(x,key=pd.isnull))
    ID    Name  val1  val2  val3
0    A   first     1     1     2
0  NaN     NaN     2     3     3
0  NaN     NaN   NaN   NaN   NaN
1    B  second     4     4     5
1  NaN     NaN     5     6     6
1  NaN     NaN   NaN   NaN   NaN

应该有效。请注意,由于sorted是一个稳定的排序,我们使用pd.isnull作为键(其中False< True),我们将NaN推到最后,同时保留其余对象的顺序。还要注意,我在这里只对索引进行分组;我们也可以根据自己的需要进行分组。

答案 1 :(得分:3)

鉴于df:

pd.DataFrame([["A","first",1.0,1.0,np.NaN],
            [np.NaN,np.NaN,2.0,np.NaN,2.0],
            [np.NaN,np.NaN,np.NaN,3.0,3.0]],
            columns=[ "ID",    "Name",    "val1",    "val2",    "val3"],index=[0,1,2])

我更改了索引以确保订单停留。

df
Out[127]: 
    ID   Name  val1  val2  val3
0    A  first     1     1   NaN
1  NaN    NaN     2   NaN     2
2  NaN    NaN   NaN     3     3

使用:

pd.concat([df[col].sort_values().reset_index(drop=True) for col in df], axis=1, ignore_index=True)

会给:

Out[130]: 
     0      1   2   3   4
0    A  first   1   1   2
1  NaN    NaN   2   3   3
2  NaN    NaN NaN NaN NaN

同样的:

df = pd.DataFrame([["A","first",1.0,1.0,np.NaN],
            [np.NaN,np.NaN,2.0,np.NaN,2.0],
            [np.NaN,np.NaN,np.NaN,3.0,3.0],
            ["B","second",4.0,4.0,np.NaN],
            [np.NaN,np.NaN,5.0,np.NaN,5.0],
             [np.NaN,np.NaN,np.NaN,6.0,6.0]],
            columns=[ "ID",    "Name",    "val1",    "val2",    "val3"],index=[0,0,0,1,1,1])

df
Out[132]: 
    ID    Name  val1  val2  val3
0    A   first     1     1   NaN
0  NaN     NaN     2   NaN     2
0  NaN     NaN   NaN     3     3
1    B  second     4     4   NaN
1  NaN     NaN     5   NaN     5
1  NaN     NaN   NaN     6     6

pd.concat([df[col].sort_values().reset_index(drop=True) for col in df], axis=1, ignore_index=True)
Out[133]: 
     0       1   2   3   4
0    A   first   1   1   2
1    B  second   2   3   3
2  NaN     NaN   4   4   5
3  NaN     NaN   5   6   6
4  NaN     NaN NaN NaN NaN
5  NaN     NaN NaN NaN NaN

发表其他评论后

new = pd.concat([df[col].sort_values().reset_index(drop=True) for col in df.iloc[:,2:]], axis=1, ignore_index=True)
new.index = df.index
cols = df.iloc[:,2:].columns
new.columns = cols
df.drop(cols,inplace=True,axis=1)
df = pd.concat([df,new],axis=1)

df
Out[37]: 
    ID    Name  val1  val2  val3
0    A   first     1     1     2
0  NaN     NaN     2     3     3
0  NaN     NaN     4     4     5
1    B  second     5     6     6
1  NaN     NaN   NaN   NaN   NaN
1  NaN     NaN   NaN   NaN   NaN

答案 2 :(得分:1)

In [219]:
df.groupby(level=0).transform(lambda x : x.sort(na_position = 'last' , inplace = False))
Out[219]:
    ID  Name    val1    val2    val3
0   A   first      1    1       2
0   NaN NaN        2    3       3
0   NaN NaN       NaN   NaN     NaN
1   B   second      4   4       5
1   NaN NaN         5   6       6
1   NaN NaN       NaN   NaN     NaN