我有一些分组的表格数据,在这个数据中有一个列,每个数据点实际上可以有一组不同的值。我正在尝试计算该集合与其前一个数据点的差异。例如,根据下面的数据,我正在尝试计算Tokens
†的值与Timestep
值 n 的差异。 Tokens
值 n 的行的Timestamp
值 - 每个Dyad,Participant
组合为1:
| Dyad | Participant | Timestep | Tokens |
|------|-------------|----------|-------------------|
| 1 | A | 1 | apple,banana |
| 1 | B | 1 | apple,orange |
| 1 | A | 2 | banana |
| 1 | B | 2 | orange,kumquat |
| 1 | A | 3 | orange |
| 1 | B | 3 | orange,pear |
| 2 | A | 1 | orange,pear |
| 2 | B | 1 | apple,banana,pear |
| 2 | A | 2 | banana,persimmon |
| 2 | B | 2 | apple |
| 2 | A | 3 | banana |
| 2 | B | 3 | apple |
如何使用熊猫最好地实现这一目标?
我最终希望使用函数token_overlap(data)
的输出创建一个新列,该函数计算Token
值与其前面数据点的值重叠的比率:
| Dyad | Participant | Timestep | Tokens | TokenOverlap |
|------|-------------|----------|-------------------| -------------|
| 1 | A | 1 | apple,banana | (no value) |
| 1 | B | 1 | apple,orange | (no value) |
| 1 | A | 2 | banana | 0.5 |
| 1 | B | 2 | orange,kumquat | 0.333 |
| 1 | A | 3 | orange | 0 |
| 1 | B | 3 | orange,pear | 0.333 |
| 2 | A | 1 | orange,pear | (no value) |
| 2 | B | 1 | apple,banana,pear | (no value) |
| 2 | A | 2 | banana,persimmon | 0 |
| 2 | B | 2 | apple | 0.333 |
| 2 | A | 3 | banana | 0.5 |
| 2 | B | 3 | apple | 1 |
我使用pandas.read_csv(...)
的frozenset
关键字将多值转换为converters
:
def parse_set(cell_value: str) -> FrozenSet[str]:
return frozenset(cell_value.split(','))
round_tokens = pandas.read_csv(inpath, converters={"Tokens": parse_set})
然后,我使用pandas.DataFrame.groupby(..)
创建Dyad,Participant
数据点组:
round_tokens.sort_values(["Dyad", "Timestep"])
dyad_participants = round_tokens.groupby(["Dyad", "Participant"])
但是,我不确定如何获取每一行及其前导者的Tokens
值(应该是frozenset
):我有一些功能尝试这样做但我不确定是否函数本身是错误的,或者我是否错误地提取行数据。
def token_overlap(data):
own_relevant_tokens = data["Tokens"]
prev_tokens = data.shift(-1)["Tokens"]
overlap = own_relevant_tokens.intersection(prev_tokens)
union = own_relevant_tokens.union(prev_tokens)
return len(overlap) / len(union)
round_tokens["TokenOverlap"] = dyad_participants.apply(token_overlap)
但是,这实际上并不起作用:实际错误是
AttributeError:'Series'对象没有属性'union'
但我知道我没有正确使用/理解/了解pandas API,因此这是一个非常冗长的问题。 如何对数据进行分组,然后在每个组中使用一行中的类似值的值和前一行的相同列值来计算指标?
†在实际数据中,Tokens
有超过1,000个可能的值,所以,至少对我而言,如果我枚举每个令牌的存在,这个任务会更难作为布尔值,例如Token_Apple
,Token_Banana
等
答案 0 :(得分:1)
<强>设置强>
df
Dyad Participant Timestep Tokens
0 1 A 1 apple,banana
1 1 B 1 apple,orange
2 1 A 2 banana
3 1 B 2 orange,kumquat
4 1 A 3 orange
5 1 B 3 orange,pear
6 2 A 1 orange,pear
7 2 B 1 apple,banana,pear
8 2 A 2 banana,persimmon
9 2 B 2 apple
10 2 A 3 banana
11 2 B 3 apple
tokens = df.Tokens.str.split(',', expand=False).apply(frozenset)
tokens
0 (apple, banana)
1 (orange, apple)
2 (banana)
3 (orange, kumquat)
4 (orange)
5 (orange, pear)
6 (orange, pear)
7 (apple, banana, pear)
8 (persimmon, banana)
9 (apple)
10 (banana)
11 (apple)
Name: Tokens, dtype: object
# union logic - https://stackoverflow.com/a/46402781/4909087
df = df.assign(Tokens=tokens)\
.groupby(['Dyad', 'Participant']).apply(\
lambda x: (x.Tokens.str.len() -
x.Tokens.diff().str.len()) \
/ pd.Series([len(k[0].union(k[1]))
for k in zip(x.Tokens, x.Tokens.shift(1).fillna(''))], index=x.index))\
.reset_index(level=[0, 1], name='TokenOverlap')\
.assign(Timestep=df.Timestep, Tokens=df.Tokens)\
.sort_values(['Dyad', 'Timestep', 'Participant'])\
.fillna('(no value)')\
[['Dyad', 'Participant', 'Timestep', 'Tokens', 'TokenOverlap']]
df
Dyad Participant Timestep Tokens TokenOverlap
0 1 A 1 apple,banana (no value)
1 1 B 1 apple,orange (no value)
2 1 A 2 banana 0.5
3 1 B 2 orange,kumquat 0.333333
4 1 A 3 orange 0
5 1 B 3 orange,pear 0.333333
6 2 A 1 orange,pear (no value)
7 2 B 1 apple,banana,pear (no value)
8 2 A 2 banana,persimmon 0
9 2 B 2 apple 0.333333
10 2 A 3 banana 0.5
11 2 B 3 apple 1
简而言之,此代码的作用是按Dyad
和Participant
进行分组,然后找到成对比率。这需要一些复杂的groupby
和apply
,因为我们需要执行一些设置union
和difference
操作。核心逻辑在groupby.apply
内,而其余的只是美化。
此代码运行于:
10 loops, best of 3: 19.2 ms per loop
<强>击穿强>
df2 = df.assign(Tokens=tokens)
df2 = df2.groupby(['Dyad', 'Participant']).apply(\
lambda x: (x.Tokens.str.len() -
x.Tokens.diff().str.len()) \
/ pd.Series([len(k[0].union(k[1]))
for k in zip(x.Tokens, x.Tokens.shift(1).fillna(''))], index=x.index)) # the for loop is part of this huge line
df2 = df2.reset_index(level=[0, 1], name='TokenOverlap')
df2 = df2.assign(Timestep=df.Timestep, Tokens=df.Tokens)
df2 = df2.sort_values(['Dyad', 'Timestep', 'Participant']).fillna('(no value)')
df2 = df2[['Dyad', 'Participant', 'Timestep', 'Tokens', 'TokenOverlap']]