这是我的pandas DataFrame:
ID START END SEQ
1 11 12 5
1 14 15 6
1 13 14 7
2 10 14 1
3 11 15 1
3 16 17 2
我想更改SEQ的值,以便对于相同的ID,SEQ值将为1,2,......等,例如
ID START END SEQ
1 11 12 1
1 14 15 3
1 13 14 2
2 10 14 1
3 11 15 1
3 16 17 2
如何有效避免循环?
答案 0 :(得分:1)
在groupby操作中使用cumcount:
df.groupby('ID').cumcount()+1
答案 1 :(得分:0)
在Boud的答案中添加一点点,如果你想要的结果是SEQ列中的顺序取决于START列的值,你可以通过
来实现这一点。df['SEQ'] = df.sort_values(by='START').groupby('ID').cumcount()+1
即,
In [3]: df
Out[3]:
ID START END SEQ
0 1 11 12 5
1 1 14 15 6
2 1 13 14 7
3 2 10 14 1
4 3 11 15 1
5 3 16 17 2
In [4]: df['SEQ'] = df.sort_values(by='START').groupby('ID').cumcount()+1
In [5]: df
Out[5]:
ID START END SEQ
0 1 11 12 1
1 1 14 15 3
2 1 13 14 2
3 2 10 14 1
4 3 11 15 1
5 3 16 17 2
答案 2 :(得分:0)
以下两种方法NumPy
使用np.cumsum
创建 ramp 数组 -
def id_ramp(a):
out = np.ones(a.size,dtype=int)
idx = np.nonzero(np.append(True,a[1:] > a[:-1]))[0]
out[idx[1:]] = -idx[1:] + idx[:-1] + 1
return out.cumsum()
def id_ramp2(a):
out = np.ones(a.size,dtype=int)
idx = np.nonzero(a[1:] > a[:-1])[0]+1
out[idx[0]] = -idx[0]+1
out[idx[1:]] = idx[:-1] - idx[1:]+1
return out.cumsum()
运行时测试 -
In [381]: a = np.sort(np.random.randint(1,100,(1000)))
In [382]: df = pd.DataFrame(a, columns=[['ID']])
In [383]: %timeit df['SEQ'] = df.groupby('ID').cumcount()+1 #@Boud's soln
100 loops, best of 3: 2.01 ms per loop
In [384]: %timeit df['SEQ'] = id_ramp(df.ID.values)
1000 loops, best of 3: 315 µs per loop
In [385]: %timeit df['SEQ'] = id_ramp2(df.ID.values)
1000 loops, best of 3: 304 µs per loop
如果您使用的ID
列并非总是排序,我们需要在那里使用一些argsort
,就像这样 -
a = df.ID.values
sidx = a.argsort(kind='mergesort')
df['SEQ'] = id_ramp2(a[sidx])[sidx.argsort()]
让我们看一个示例案例,看看它是如何工作的 -
In [447]: df
Out[447]:
ID
0 1
1 1
2 7
3 5
4 3
5 8
6 1
7 3
8 7
9 2
10 5
11 7
In [448]: a = df.ID.values
...: sidx = a.argsort(kind='mergesort')
...: df['SEQ'] = id_ramp2(a[sidx])[sidx.argsort()]
...:
In [449]: df
Out[449]:
ID SEQ
0 1 1
1 1 2
2 7 1
3 5 1
4 3 1
5 8 1
6 1 3
7 3 2
8 7 2
9 2 1
10 5 2
11 7 3