我有一个大数据框(500K行x 100列),并且想有效地执行以下搜索和掩蔽操作,但是我找不到正确的熊猫/ numpy咒语;如果可以向量化效果更好:
m1,m2,...,m6
可以包含与1..9或末尾NaN不同的值。 (存在NaN的理由非常充分,以防止在我们处理此步骤的输出时对不存在的记录进行汇总/取和/均值/等;非常希望保留NaN)
m<i>
最多包含一个出现的值1..9 x1,x2,...,x6
与列m<i>
关联,并且包含一些整数值v
(我将在分析的顶层从1:9手动扫描v,不用担心这一部分),我想执行以下操作:
v
之一中出现值m<i>
的每一行上,找到哪一列m<i>
等于v
(布尔布尔掩码/数组/索引/任何东西)否则,您更喜欢)v
中未发生m<i>
的行上,最好我不希望该行有任何结果,甚至不希望NaN x<i>
(x1,x2,...,x6
)中切出相应的值这是我当前的代码;我尝试了iloc
,melt
,stack/unstack
,mask
,np.where
,np.select
和其他方法,但无法获得预期的结果:< / p>
import numpy as np
from numpy import nan
import pandas as pd
N = 6 # the width of our column-slices of interest
# Sample dataframe
dat = pd.compat.StringIO("""
text,m1,m2,m3,m4,m5,m6,x1,x2,x3,x4,x5,x6\n
'foo',9,3,4,2,1,, 21,22,23,24,25,26\n
'bar',2,3,4,6,5,, 31,32,33,34,35,36\n
'baz',7,3,4,1,,, 11,12,13,14,15,16\n
'qux',2,6,3,4,7,, 41,42,43,44,45,46\n
'gar',3,1,4,7,,, 51,52,53,54,55,56\n
'wal',3,,,,,, 11,12,13,14,15,16\n
'fre',2,3,4,6,5,, 61,62,63,64,65,66\n
'plu',2,3,4,9,1,, 71,72,73,74,75,76\n
'xyz',2,3,4,9,6,1, 81,82,83,84,85,86\n
'thu',1,3,6,4,5,, 51,52,53,54,55,56""".replace(' ',''))
df = pd.read_csv(dat, header=[0])
v = 1 # For example; Actually we want to sweep v from 1:9 ...
# On each row, find the index 'i' of column 'm<i>' which equals v; or NaN if v doesn't occur
df.iloc[:, 1:N+1] == v
(df.iloc[:, 1:N+1] == 1).astype(np.int64)
# m1 m2 m3 m4 m5 m6
# 0 0 0 0 0 1 0
# 1 0 0 0 0 0 0
# 2 0 0 0 1 0 0
# 3 0 0 0 0 0 0
# 4 0 1 0 0 0 0
# 5 0 0 0 0 0 0
# 6 0 0 0 0 0 0
# 7 0 0 0 0 1 0
# 8 0 0 0 0 0 1
# 9 1 0 0 0 0 0
# np.where() seems useful...
_ = np.where((df.iloc[:, 1:N+1] == 1).astype(np.int64))
# (array([0, 2, 4, 7, 8, 9]), array([4, 3, 1, 4, 5, 0]))
# But you can't directly use df.iloc[ np.where((df.iloc[:, 1:N+1] == 1).astype(np.int64)) ]
# Feels like you want something like df.iloc[ *... ] where we can pass in our intermediate result as separate vectors of row- and col-indices
# can't unpack the np.where output into separate row- and col- indices vectors
irow,icol = *np.where((df.iloc[:, 1:N+1] == 1).astype(np.int64))
SyntaxError: can't use starred expression here
# ...so unpack manually...
irow = _[0]
icol = _[1]
# ... but now can't manage to slice the `x<i>` with those...
df.iloc[irow, 7:13] [:, icol.tolist()]
TypeError: unhashable type: 'slice'
# Want to get numpy-type indexing, rather than pandas iloc[]
# This also doesn't work:
df.iloc[:, 7:13] [list(zip(*_))]
# Want to slice into the x<i> which are located in df.iloc[:, N+1:2*N+1]
# Or any alternative faster numpy/pandas implementation...
答案 0 :(得分:1)
为了便于阅读,并避免在 df 中使用 float 表示法,我首先使用 以下指令将 NaN 值更改为0并将其类型更改为 int :
df.fillna(0, downcast='infer', inplace=True)
现在开始进行主要任务,因为 v == 1 。开始于:
x1 = np.argwhere(df.iloc[:, 1:N+1].values == v)
结果是:
[[0 4]
[2 3]
[4 1]
[7 4]
[8 5]
[9 0]]
它们是 df 子集中元素 == v 的索引。
然后,要“转移”到整个 df 中的 target 元素的索引, 我们必须在每个列索引中添加 7 (实际上是 N + 1 ):
x2 = x1 + [0, N+1]
结果是:
[[ 0 11]
[ 2 10]
[ 4 8]
[ 7 11]
[ 8 12]
[ 9 7]]
并获取结果(对于 v == 1 ),执行:
df.values[tuple(x2.T)]
结果是:
array([25, 14, 52, 75, 86, 51], dtype=object)
替代方法:如果希望在单指令中获得以上结果,请运行:
df.values[tuple((np.argwhere(df.iloc[:, 1:N+1].values == v) + [0, N+1]).T)]
上述过程给出了 v == 1 的结果。 如何将每次通过的结果(对于 v = 1..9 )汇总到 最后结果。您没有在问题中描述这个细节(否则我失败了 来了解它)。
可能的解决方案之一是:
pd.DataFrame([ df.values[tuple((np.argwhere(df.iloc[:, 1:N+1].values
== v) + [0, N+1]).T)].tolist() for v in range(1,10) ],
index=range(1,10)).fillna('-')
给出以下结果:
0 1 2 3 4 5 6 7 8 9
1 25 14 52 75 86 51 - - - -
2 24 31 41 61 71 81 - - - -
3 22 32 12 43 51 11 62 72 82 52
4 23 33 13 44 53 63 73 83 54 -
5 35 65 55 - - - - - - -
6 34 42 64 85 53 - - - - -
7 11 45 54 - - - - - - -
8 - - - - - - - - - -
9 21 74 84 - - - - - - -
索引值取自 v 的当前值。 是否对默认感到满意取决于您 列名称(从0开始的连续数字)。
附加说明:删除第一个中的撇号周围的值 列(例如,将'foo'更改为 foo )。 否则这些撇号是列内容的一部分,我想 你不要 请注意,例如源列第一行中的名称为不包含 撇号和 read_csv 足够聪明,可以将它们识别为 string 值。
另一个可能更简单的解决方案:
因为我们在基础 NumPy 表上进行操作,而不是 .values 从许多方面出发,以:
tbl = df.values
然后,对于单个 v 值,使用 argwhere
代替 nonzero
:
tbl[:, N+1:][np.nonzero(tbl[:, 1:N+1] == v)]
详细信息:
tbl[:, 1:N+1]
- m ... 列的切片。np.nonzero(tbl[:, 1:N+1] == v)
-列表的元组-的索引
“所需”元素(按轴分组),因此可以直接
用于索引编制。tbl[:, N+1:]
- x<i>
列的切片。 nonzero
和 argwhere
之间的重要区别是
nonzero
返回一个元组,因此在
列号比较困难,所以我决定采用其他方式
切片(用于 {x<i>
列)。