我为我的每个在线学生都有一个数据框架,形状如下:
df=pd.DataFrame(columns=["Class","Student","Challenge","Time"])
我为每个这样的挑战创建了一个临时df:
for ch in challenges:
df=main_df.loc[(df['Challenge'] == ch)]
我想把相隔5分钟的唱片分组。我的想法是,我将创建一个df,显示一群学生在相对相同的时间一起工作并回答问题。我在捕捉作弊者方面有不同的方面,但我需要能够证明两个或两个以上的学生在相对相同的时间回答了问题。
我曾想过使用重采样或grouper方法,但我对如何实现它有点困惑。有人能指导我解决这个问题的正确方向吗?
样品df:
df = pd.DataFrame(
data={
"Class": ["A"] * 4 + ["B"] * 2,
"Student": ['Scooby','Daphne','Shaggy','Fred','Velma','Scrappy'],
"Challenge": ["Challenge3"] *6,
"Time": ['07/10/2022 08:22:44','07/10/2022 08:27:22','07/10/2022 08:27:44','07/10/2022 08:29:55','07/10/2022 08:33:14','07/10/2022 08:48:44'],
}
)
编辑:
对于输出,我考虑使用另一个df,该df带有一个名为"Grouping"的额外列,并为发现的组提供一个递增编号。最后的df将首先为空,然后附加或连接分组df。
new_df = pd.dataframe(columns=['Class','Student','Challenge','Time','Grouping'])
new_df = pd.concat([new_df,df])
Class Student Challenge Time Grouping
1 A Daphne Challenge3 07/10/2022 08:27:22 1
2 A Shaggy Challenge3 07/10/2022 08:27:44 1
3 A Fred Challenge3 07/10/2022 08:29:55 1
这样做的目的是为了让我可以有多个样本来验证同一个两个或多个学生是否在分享答案。
编辑2:
我在想我可以做一个基于lambda的运算。如果我创建了另一个名为";阈值Delta";计算一个答案与另一个答案的时间差?然后我需要弄清楚如何将这些分钟分组。
df['Challenge_Delta'] = (df['Time']-df['Time'].shift())
此解决方案实现了一个滚动时间列表。逻辑是这样的——只要列表中的所有条目都在时间窗口内,就继续添加条目。当你检测到一些条目不在相对于最新(要添加的(条目的时间窗口中时,这表明你有一个唯一的作弊者(可能(组。然后将该组写为列表,删除窗口外的条目并添加最新的条目。最后,列表上可能会留下一组条目。dump_last()
允许该集合被拉取。在你有了这些列表之后,你可以进行一大堆作弊检测分析。
确保Time
列为datetime
df['Time'] = df['Time'].astype('datetime64[ns]')
滚动时间列表类定义
class RollingTimeList:
def __init__(self):
self.cur = pd.DataFrame(columns=['Student','Time'])
self.window = pd.Timedelta('5T')
def __add(self, row):
idx = self.cur.index.max()
new_idx = idx+1 if idx==idx else 0
self.cur.loc[new_idx] = row[['Student','Time']]
def handle_row(self, row):
rc = None
if len(self.cur) > 0:
window_mask = (row['Time'] - self.cur['Time']).abs() <= self.window
if ~window_mask.all():
if len(self.cur) > 1:
rc = self.cur['Student'].to_list()
self.cur = self.cur.loc[window_mask]
self.__add(row)
return rc
def dump_last(self):
rc = None
if len(self.cur) > 1:
rc = self.cur['Student'].to_list()
self.cur = self.cur[0:0]
return rc
实例化并应用类
rolling_list = RollingTimeList()
s = df.apply(rolling_list.handle_row, axis=1)
idx = s.index.max()
s.loc[idx+1 if idx==idx else 0] = rolling_list.dump_last()
print(s.dropna())
结果
3 [Scooby, Daphne, Shaggy]
4 [Daphne, Shaggy, Fred]
5 [Fred, Velma]
将其合并回原始数据帧
df['window_groups'] = s
df['window_groups'] = df['window_groups'].shift(-1).fillna('')
print(df)
结果
Class Student Challenge Time window_groups
0 A Scooby Challenge3 2022-07-10 08:22:44
1 A Daphne Challenge3 2022-07-10 08:27:22
2 A Shaggy Challenge3 2022-07-10 08:27:44 [Scooby, Daphne, Shaggy]
3 A Fred Challenge3 2022-07-10 08:29:55 [Daphne, Shaggy, Fred]
4 B Velma Challenge3 2022-07-10 08:33:14 [Fred, Velma]
5 B Scrappy Challenge3 2022-07-10 08:48:44
可视化思想:交叉引用表
dfd = pd.get_dummies(s.dropna().apply(pd.Series).stack()).groupby(level=0).sum()
xrf = dfd.T.dot(dfd)
print(xrf)
结果
Daphne Fred Scooby Shaggy Velma
Daphne 2 1 1 2 0
Fred 1 2 0 1 1
Scooby 1 0 1 1 0
Shaggy 2 1 1 2 0
Velma 0 1 0 0 1
或作为一系列独特的学生组合和计数
combos = xrf.stack()
unique_combo_mask = combos.index.get_level_values(0)<combos.index.get_level_values(1)
print(combos[unique_combo_mask])
结果
Daphne Fred 1
Scooby 1
Shaggy 2
Velma 0
Fred Scooby 0
Shaggy 1
Velma 1
Scooby Shaggy 1
Velma 0
Shaggy Velma 0
此外,从这里,您可以确定在某种时间约束(或其他约束(内,由于列表重叠,计数实际上不应超过一。就像这个例子一样,Daphne和Shaggy可能不应该被计算两次。他们在两个不同的场合并没有真正在5分钟内到达。在这种情况下,任何大于1的东西都可以在累积到更大的池中之前设置为1。