在pandas DataFrame中向左移动非空单元格

时间:2015-08-18 01:26:41

标签: python pandas

假设我有

形式的数据
Name    h1    h2    h3    h4
A       1     nan   2     3
B       nan   nan   1     3
C       1     3     2     nan

我想将所有非纳米细胞向左移动(或收集新列中的所有非纳米数据),同时保留从左到右的顺序,得到

Name    h1    h2    h3    h4
A       1     2     3     nan
B       1     3     nan   nan
C       1     3     2     nan

我当然可以一行一行地这样做。但我希望知道是否还有其他方法可以提高性能。

4 个答案:

答案 0 :(得分:5)

首先,制作功能。

        def squeeze_nan(x):
            original_columns = x.index.tolist()

            squeezed = x.dropna()
            squeezed.index = [original_columns[n] for n in range(squeezed.count())]

            return squeezed.reindex(original_columns, fill_value=np.nan)

其次,应用该功能。

df.apply(squeeze_nan, axis=1)

你也可以尝试 axis = 0 。[:: - 1] 来向任何方向挤压nan。

<强> [编辑]

@ Mxracer888你想要这个吗?

def squeeze_nan(x, hold):
    if x.name not in hold:
        original_columns = x.index.tolist()

        squeezed = x.dropna()
        squeezed.index = [original_columns[n] for n in range(squeezed.count())]

        return squeezed.reindex(original_columns, fill_value=np.nan)
    else:
        return x

df.apply(lambda x: squeeze_nan(x, ['B']), axis=1)

enter image description here

答案 1 :(得分:2)

这就是我的所作所为:

我将您的数据帧拆分为更长的格式,然后按名称列进行分组。在每组中,我丢弃NaN,但随后重新索引到完整的h1思想h4集,从而重新创建你的NaN到右边。

from io import StringIO
import pandas

def defragment(x):
    values = x.dropna().values
    return pandas.Series(values, index=df.columns[:len(values)])

datastring = StringIO("""\
Name    h1    h2    h3    h4
A       1     nan   2     3
B       nan   nan   1     3
C       1     3     2     nan""")

df = pandas.read_table(datastring, sep='\s+').set_index('Name')
long_index = pandas.MultiIndex.from_product([df.index, df.columns])

print(
    df.stack()
      .groupby(level='Name')
      .apply(defragment)
      .reindex(long_index)  
      .unstack()  
)

所以我得到了:

   h1  h2  h3  h4
A   1   2   3 NaN
B   1   3 NaN NaN
C   1   3   2 NaN

答案 2 :(得分:2)

以下是使用正则表达式(可能不推荐)的方法:

pd.read_csv(StringIO(re.sub(',+',',',df.to_csv())))
Out[20]: 
  Name  h1  h2  h3  h4
0    A   1   2   3 NaN
1    B   1   3 NaN NaN
2    C   1   3   2 NaN

答案 3 :(得分:1)

首先,使用 np.isnan 创建一个布尔数组,这会将 NaN 标记为 True,将非 nan 值标记为 False,然后对它们进行 argsort,这样您将保持非 - nan 值和 NaN 被推到右边。

idx = np.isnan(df.values).argsort(axis=1)
df = pd.DataFrame(
    df.values[np.arange(df.shape[0])[:, None], idx],
    index=df.index,
    columns=df.columns,
)

       h1   h2   h3  h4
Name
A     1.0  2.0  3.0 NaN
B     1.0  3.0  NaN NaN
C     1.0  3.0  2.0 NaN

详情

np.isnan(df.values)
# array([[False,  True, False, False],
#        [ True,  True, False, False],
#        [False, False, False,  True]])

# False ⟶ 0 True ⟶ 1
# When sorted all True values i.e nan are pushed to the right.

idx = np.isnan(df.values).argsort(axis=1)
# array([[0, 2, 3, 1],
#        [2, 3, 0, 1],
#        [0, 1, 2, 3]], dtype=int64)

# Now, indexing `df.values` using `idx`
df.values[np.arange(df.shape[0])[:, None], idx]
# array([[ 1.,  2.,  3., nan],
#        [ 1.,  3., nan, nan],
#        [ 1.,  3.,  2., nan]])

# Make that as a DataFrame
df = pd.DataFrame(
    df.values[np.arange(df.shape[0])[:, None], idx],
    index=df.index,
    columns=df.columns,
)

#        h1   h2   h3  h4
# Name
# A     1.0  2.0  3.0 NaN
# B     1.0  3.0  NaN NaN
# C     1.0  3.0  2.0 NaN