在Pandas数据框中,查找每行非零值的列索引集合。

16

有没有一种好的方法可以找到pandas数据帧中每行非零值的列索引集合?我必须逐行遍历数据帧吗?

例如,数据帧如下:

c1  c2  c3  c4 c5 c6 c7 c8  c9
 1   1   0   0  0  0  0  0   0
 1   0   0   0  0  0  0  0   0
 0   1   0   0  0  0  0  0   0
 1   0   0   0  0  0  0  0   0
 0   1   0   0  0  0  0  0   0
 0   0   0   0  0  0  0  0   0
 0   2   1   1  1  1  1  0   2
 1   5   5   0  0  1  0  4   6
 4   3   0   1  1  1  1  5  10
 3   5   2   4  1  2  2  1   3
 6   4   0   1  0  0  0  0   0
 3   9   1   0  1  0  2  1   0

预期输出为:

['c1','c2']
['c1']
['c2']
...
4个回答

13

看起来你需要逐行遍历DataFrame。

cols = df.columns
bt = df.apply(lambda x: x > 0)
bt.apply(lambda x: list(cols[x.values]), axis=1)

你将得到:

0                                 [c1, c2]
1                                     [c1]
2                                     [c2]
3                                     [c1]
4                                     [c2]
5                                       []
6             [c2, c3, c4, c5, c6, c7, c9]
7                 [c1, c2, c3, c6, c8, c9]
8         [c1, c2, c4, c5, c6, c7, c8, c9]
9     [c1, c2, c3, c4, c5, c6, c7, c8, c9]
10                            [c1, c2, c4]
11                [c1, c2, c3, c5, c7, c8]
dtype: object

如果性能很重要,尝试像下面这样将raw=True传递给布尔数据框的创建:

如果性能很重要,尝试像下面这样将raw=True传递给布尔数据框的创建:

%timeit df.apply(lambda x: x > 0, raw=True).apply(lambda x: list(cols[x.values]), axis=1)
1000 loops, best of 3: 812 µs per loop

它将带给您更好的性能增益。以下是使用raw=False(默认值)的结果:

%timeit df.apply(lambda x: x > 0).apply(lambda x: list(cols[x.values]), axis=1)
100 loops, best of 3: 2.59 ms per loop

3

可能更好的数据结构(而不是列表系列)是堆叠:

In [11]: res = df[df!=0].stack()

In [12]: res
Out[12]:
0   c1     1
    c2     1
1   c1     1
2   c2     1
3   c1     1
...

您可以迭代原始行:

In [13]: res.loc[0]
Out[13]:
c1    1
c2    1
dtype: float64

In [14]: res.loc[0].index
Out[14]: Index(['c1', 'c2'], dtype='object')

注意:我认为以前可以在apply中返回一个列表(创建具有列表元素的DataFrame),但现在似乎不行了。


2

这个方法怎么样?

#create a True / False data frame
df_boolean = df>0

#a little helper method that uses boolean slicing internally 
def bar(x,columns):
    return ','.join(list(columns[x]))

#use an apply along the column axis
df_boolean['result'] = df_boolean.apply(lambda x: bar(x,df_boolean.columns),axis=1)

# filter out the empty "rows" adn grab the result column
df_result =  df_boolean[df_boolean['result'] != '']['result']

#append an axis, just so each line will will output a list 
lst_result = df_result.values[:,np.newaxis]

print '\n'.join([ str(myelement) for myelement in lst_result])

这将产生:

['c1,c2']
['c1']
['c2']
['c1']
['c2']
['c2,c3,c4,c5,c6,c7,c9']
['c1,c2,c3,c6,c8,c9']
['c1,c2,c4,c5,c6,c7,c8,c9']
['c1,c2,c3,c4,c5,c6,c7,c8,c9']
['c1,c2,c4']
['c1,c2,c3,c5,c7,c8']

Scari已经比我更快地给出了一个更简洁的解决方案。我可以保留我的答案在线以演示输出,或者很乐意将其删除。 - Dickster

0

如果你只想定位非零值,numpy.argwhere()和nonzero()都是一行代码。

nzero = np.argwhere(df.to_numpy())
# nzero is an array of two-element arrays [irow, icol]
nz = df.to_numpy().nonzero()
# Alternatively, nz is a duple of numpy 1D-arrays of corresponding indices

但是为了获得所需的逐行输出,我想不到避免对行进行循环的方法。被接受的答案要短得多。

pairit = iter(nzero)
pair = next(pairit)
for irow in range(len(df)):
    # want one list for each row
    cols = []
    while pair[0] == irow:
        cols.append(df.columns[pair[1]])
        try:
            pair = next(pairit)
        except StopIteration:
            break
    print(irow, cols)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接