假设我有一个熊猫数据框
rid category
0 0 c2
1 1 c3
2 2 c2
3 3 c3
4 4 c2
5 5 c2
6 6 c1
7 7 c3
8 8 c1
9 9 c3
我想添加2列pid和nid,以便对于每一行pid包含与rid属于同一类别的随机ID(除rid之外),而nid包含与rid属于不同类别的随机ID ,
示例数据框为:
rid category pid nid
0 0 c2 2 1
1 1 c3 7 4
2 2 c2 0 1
3 3 c3 1 5
4 4 c2 5 7
5 5 c2 4 6
6 6 c1 8 5
7 7 c3 9 8
8 8 c1 6 2
9 9 c3 1 2
请注意,pid不应与rid相同。现在,我只是通过遍历行并每次采样来强制执行它,这似乎效率很低。
有更好的方法吗?
编辑1:为简单起见,我们假设每个类别至少代表两次,以便可以找到至少一个ID,该ID并非摆脱但具有相同的类别。
编辑2:为进一步简化,我们假设在一个大数据框中,以与rid相同的id结束的概率为零。如果是这样,我相信解决方案应该会更容易。我会宁愿不做这个假设,尽管
答案 0 :(得分:2)
对于pid列,请使用Sattolo's algorithm
,对于nid
,请获取所有可能的差值,其中列的所有数量与组的值之间的差值分别为numpy.random.choice
和set
:
from random import randrange
#https://stackoverflow.com/questions/7279895
def sattoloCycle(items):
items = list(items)
i = len(items)
while i > 1:
i = i - 1
j = randrange(i) # 0 <= j <= i-1
items[j], items[i] = items[i], items[j]
return items
def outsideGroupRand(x):
return np.random.choice(list(set(df['rid']).difference(x)),
size=len(x),
replace=False)
df['pid1'] = df.groupby('category')['rid'].transform(sattoloCycle)
df['nid1'] = df.groupby('category')['rid'].transform(outsideGroupRand)
print (df)
rid category pid nid pid1 nid1
0 0 c2 2 1 4 6
1 1 c3 7 4 7 4
2 2 c2 0 1 5 3
3 3 c3 1 5 1 0
4 4 c2 5 7 2 9
5 5 c2 4 6 0 8
6 6 c1 8 5 8 3
7 7 c3 9 8 9 5
8 8 c1 6 2 6 5
9 9 c3 1 2 3 6
答案 1 :(得分:1)
import pandas as pd
import numpy as np
## generate dummy data
raw = {
"rid": range(10),
"cat": np.random.choice("c1,c2,c3".split(","), 10)
}
df = pd.DataFrame(raw)
def get_random_ids(x):
pids,nids = [],[]
sh = x.copy()
for _ in x:
## do circular shift choose random value except cur_val
cur_value = sh.iloc[0]
sh = sh.shift(-1)
sh[-1:] = cur_value
pids.append(np.random.choice(sh[:-1]))
## randomly choose from values from other cat
nids = np.random.choice(df[df["cat"]!=x.name]["rid"], len(x))
return pd.DataFrame({"pid": pids, "nid": nids}, index=x.index)
new_ids = df.groupby("cat")["rid"].apply(lambda x:get_random_ids(x))
df.join(new_ids).sort_values("cat")
输出
rid cat pid nid
5 5 c1 8.0 9
8 8 c1 5.0 6
0 0 c2 6.0 1
2 2 c2 0.0 8
3 3 c2 0.0 9
6 6 c2 2.0 4
7 7 c2 3.0 1
1 1 c3 9.0 5
4 4 c3 9.0 0
9 9 c3 4.0 2
答案 2 :(得分:1)
从定义计算 pid 的函数开始:
def getPid(elem, grp):
return grp[grp != elem].sample().values[0]
参数:
想法是:
然后定义第二个函数,生成两个新的id:
def getIds(grp):
pids = grp.rid.apply(getPid, grp=grp.rid)
rowNo = grp.rid.size
currGrp = grp.name
nids = df.query('category != @currGrp').rid\
.sample(rowNo, replace=True)
return pd.DataFrame({'pid': pids, 'nid': nids.values}, index=grp.index)
请注意:
但是 pid 值必须分别进行计算,并对每个值应用 getPid 当前组的元素( rid )。
原因是每次都应消除不同的元素 从当前组中调用 sample 之前。
要获取结果,请运行单指令:
pd.concat([df, df.groupby('category').apply(getIds)], axis=1)