在 pandas 列的切片中搜索值出现次数,获取 indices/mask 次出现次数,然后使用它索引到第二个列切片中
Search slice of pandas columns for value occurrence, get indices/mask of occurrences, then use that to index into a second slice of columns
我有一个大数据框(500K 行 x 100 列)并且 想高效地执行以下搜索和屏蔽操作,但我找不到正确的 pandas/numpy 咒语;如果可以矢量化就更好了:
- 在每一行上,N 列
m1,m2,...,m6
可以包含来自 1..9 或尾随 NaN 的不同值。 (NaN 在那里是有充分理由的,以防止 aggregation/taking sum/mean/etc. 在我们处理此步骤的输出时出现不存在的记录;强烈希望您保留 NaN)
- 区别性:保证列
m<i>
最多包含每个值 1..9 的一次出现
- 列
x1,x2,...,x6
与列 m<i>
关联,并包含一些整数值
- 对于范围 1..9 中的每个可能值
v
(我将在分析的顶层从 1:9 手动扫描 v,不用担心那部分),我想做以下事情:
- 在值
v
出现在 m<i>
之一的每一行上,找出哪一列 m<i>
等于 v
(布尔值 mask/array/indices/anything否则你更喜欢)
- 在
m<i>
中没有出现 v
的行上,最好我不希望该行有任何结果,甚至 NaN
- 然后我想使用该中间布尔值 mask/array/indices/whatever 从该行
x<i>
(x1,x2,...,x6
) 中切出相应的值
这是我当前的代码;我尝试了 iloc
、melt
、stack/unstack
、mask
、np.where
、np.select
和其他方法,但无法获得所需的结果:
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...
为了可读性,并避免在 df 中使用 float 符号,我首先使用
以下指令将 NaN 值更改为 0 并将其类型更改为 int:
df.fillna(0, downcast='infer', inplace=True)
解决方案 1
现在开始执行主要任务,因为 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 的索引
然后,"shift"到target元素的索引,在wholedf,
我们必须向每个列索引添加 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 的结果。
这取决于你如何assemble 每次传递的结果(对于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)。
否则这些撇号是列内容的一部分,我想
你不想要它。
请注意,例如在源列名称的第一行是 without
撇号和 read_csv 足够聪明,可以将它们识别为 string
值。
编辑 - 解决方案 2
另一个可能更简单的解决方案:
因为我们对基础 NumPy table 进行操作,而不是 .values
在许多方面,开始于:
tbl = df.values
然后,对于单个 v 值,使用 [=23= 而不是 argwhere
]:
tbl[:, N+1:][np.nonzero(tbl[:, 1:N+1] == v)]
详情:
tbl[:, 1:N+1]
- m... 列的切片。
np.nonzero(tbl[:, 1:N+1] == v)
- 列表的 元组 - 索引
"wanted" 个元素,按轴分组,所以可以直接
用于索引。
tbl[:, N+1:]
- x<i>
列的切片。
nonzero
和 argwhere
之间的一个重要区别是
nonzero
returns 一个 元组 所以添加一个 "shift" 值到
列号比较难,所以我决定换一个
切片(对于 x<i>
列)。
我有一个大数据框(500K 行 x 100 列)并且 想高效地执行以下搜索和屏蔽操作,但我找不到正确的 pandas/numpy 咒语;如果可以矢量化就更好了:
- 在每一行上,N 列
m1,m2,...,m6
可以包含来自 1..9 或尾随 NaN 的不同值。 (NaN 在那里是有充分理由的,以防止 aggregation/taking sum/mean/etc. 在我们处理此步骤的输出时出现不存在的记录;强烈希望您保留 NaN)- 区别性:保证列
m<i>
最多包含每个值 1..9 的一次出现
- 区别性:保证列
- 列
x1,x2,...,x6
与列m<i>
关联,并包含一些整数值 - 对于范围 1..9 中的每个可能值
v
(我将在分析的顶层从 1:9 手动扫描 v,不用担心那部分),我想做以下事情:- 在值
v
出现在m<i>
之一的每一行上,找出哪一列m<i>
等于v
(布尔值 mask/array/indices/anything否则你更喜欢) - 在
m<i>
中没有出现v
的行上,最好我不希望该行有任何结果,甚至 NaN - 然后我想使用该中间布尔值 mask/array/indices/whatever 从该行
x<i>
(x1,x2,...,x6
) 中切出相应的值
- 在值
这是我当前的代码;我尝试了 iloc
、melt
、stack/unstack
、mask
、np.where
、np.select
和其他方法,但无法获得所需的结果:
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...
为了可读性,并避免在 df 中使用 float 符号,我首先使用 以下指令将 NaN 值更改为 0 并将其类型更改为 int:
df.fillna(0, downcast='infer', inplace=True)
解决方案 1
现在开始执行主要任务,因为 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 的索引然后,"shift"到target元素的索引,在wholedf, 我们必须向每个列索引添加 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 的结果。 这取决于你如何assemble 每次传递的结果(对于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)。 否则这些撇号是列内容的一部分,我想 你不想要它。 请注意,例如在源列名称的第一行是 without 撇号和 read_csv 足够聪明,可以将它们识别为 string 值。
编辑 - 解决方案 2
另一个可能更简单的解决方案:
因为我们对基础 NumPy table 进行操作,而不是 .values 在许多方面,开始于:
tbl = df.values
然后,对于单个 v 值,使用 [=23= 而不是 argwhere
]:
tbl[:, N+1:][np.nonzero(tbl[:, 1:N+1] == v)]
详情:
tbl[:, 1:N+1]
- m... 列的切片。np.nonzero(tbl[:, 1:N+1] == v)
- 列表的 元组 - 索引 "wanted" 个元素,按轴分组,所以可以直接 用于索引。tbl[:, N+1:]
-x<i>
列的切片。
nonzero
和 argwhere
之间的一个重要区别是
nonzero
returns 一个 元组 所以添加一个 "shift" 值到
列号比较难,所以我决定换一个
切片(对于 x<i>
列)。