pandas sample based on category for each row
假设我有一个熊猫数据框
1 2 3 4 5 6 7 8 9 10 11 | 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,
一个示例数据框将是:
1 2 3 4 5 6 7 8 9 10 11 | 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结束的概率为零。 如果是这样,我相信解决方案应该会更容易。 我宁愿不要做这个假设
对于pid列,请使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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 |
首先定义一个函数计算pid:
1 2 | def getPid(elem, grp): return grp[grp != elem].sample().values[0] |
参数:
- eleme-当前摆脱群组的人,
- grp-整组rid值。
这个想法是:
- 从当前组(针对某些类别)中选择"其他"元素,
- 呼叫样本
- 返回样本返回的系列中唯一的返回值。
然后定义第二个函数,生成两个新的id:
1 2 3 4 5 6 7 | 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) |
注意:
-
当前组的所有nid值都可以使用
一个电话来取样, - 来自"其他类别"的一系列摆脱。
但是pid值必须单独计算,将getPid应用于每个
当前组的元素(摆脱)。
原因是每次都应消除不同的元素
从当前组中调用样本之前。
为了得到结果,运行一条指令:
1 | pd.concat([df, df.groupby('category').apply(getIds)], axis=1) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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") |
输出
1 2 3 4 5 6 7 8 9 10 11 | 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 |