Joh*_*hnH 4 python dataframe pandas
我有一个从 Pandas 延伸出来的问题:条件滚动计数。我想在数据框中创建一个新列,反映满足多个条件的累积行数。
使用以下示例和来自 stackoverflow 25119524 的代码
import pandas as pd
l1 =["1", "1", "1", "2", "2", "2", "2", "2"]
l2 =[1, 2, 2, 2, 2, 2, 2, 3]
l3 =[45, 25, 28, 70, 95, 98, 120, 80]
cowmast = pd.DataFrame(list(zip(l1, l2, l3)))
cowmast.columns =['Cow', 'Lact', 'DIM']
def rolling_count(val):
if val == rolling_count.previous:
rolling_count.count +=1
else:
rolling_count.previous = val
rolling_count.count = 1
return rolling_count.count
rolling_count.count = 0 #static variable
rolling_count.previous = None #static variable
cowmast['xmast'] = cowmast['Cow'].apply(rolling_count) #new column in dataframe
cowmast
Run Code Online (Sandbox Code Playgroud)
输出是每头牛的 xmast(乳腺炎次数)
奶牛乳 DIM xmast 0 1 1 45 1 1 1 2 25 2 2 1 2 28 3 3 2 2 70 1 4 2 2 95 2 5 2 2 98 3 6 2 2 120 4 7 2 3 80 5
我想做的是重新开始计算每头奶牛的哺乳期 (Lact),并且仅当行之间的天数 (DIM) 超过 7 时才增加计数。
为了合并多个条件来重置每头奶牛泌乳期 (Lact) 的计数,我使用了以下代码。
def count_consecutive_items_n_cols(df, col_name_list, output_col):
cum_sum_list = [
(df[col_name] != df[col_name].shift(1)).cumsum().tolist() for col_name in col_name_list
]
df[output_col] = df.groupby(
["_".join(map(str, x)) for x in zip(*cum_sum_list)]
).cumcount() + 1
return df
count_consecutive_items_n_cols(cowmast, ['Cow', 'Lact'], ['Lxmast'])
Run Code Online (Sandbox Code Playgroud)
这会产生以下输出
奶牛乳 DIM xmast Lxmast 0 1 1 45 1 1 1 1 2 25 2 1 2 1 2 28 3 2 3 2 2 70 1 1 4 2 2 95 2 2 5 2 2 98 3 3 6 2 2 120 4 4 7 2 3 80 5 1
我希望了解如何在累积计数中添加另一个条件,其中考虑到乳腺炎事件之间的时间(同一乳汁中奶牛的行之间的 DIM 差异)。如果同一头奶牛和哺乳期的行之间的 DIM 差异小于 7,则计数不应增加。
我正在寻找的输出在下表中称为“已调整”。
奶牛 Lact DIM xmast Lxmast 已调整 0 1 1 45 1 1 1 1 1 2 25 2 1 1 2 1 2 28 3 2 1 3 2 2 70 1 1 1 4 2 2 95 2 2 2 5 2 2 98 3 3 2 6 2 2 120 4 4 3 7 2 3 80 5 1 1
在上述奶牛 1 乳 2 的示例中,当暗度从 25 变为 28 时,计数不会增加,因为两个事件之间的差异小于 7 天。当奶牛 2 乳 2 从 95 增加到 98 时,情况也是如此。对于较大的增量(70 到 95 以及 98 到 120),计数会增加。
感谢您的帮助
约翰
实际上,如果您使用了所引用问题中得票最高的解决方案,那么您要设置的代码可以大大xmast简化。Lxmast
将数据框重命名cowmast为df,您可以按如下方式设置xmast:
df['xmast'] = df.groupby((df['Cow'] != df['Cow'].shift(1)).cumsum()).cumcount()+1
Run Code Online (Sandbox Code Playgroud)
同样,要设置Lxmast,您可以使用:
df['Lxmast'] = (df.groupby([(df['Cow'] != df['Cow'].shift(1)).cumsum(),
(df['Lact'] != df['Lact'].shift()).cumsum()])
.cumcount()+1
)
Run Code Online (Sandbox Code Playgroud)
数据输入
l1 =["1", "1", "1", "2", "2", "2", "2", "2"]
l2 =[1, 2, 2, 2, 2, 2, 2, 3]
l3 =[45, 25, 28, 70, 95, 98, 120, 80]
cowmast = pd.DataFrame(list(zip(l1, l2, l3)))
cowmast.columns =['Cow', 'Lact', 'DIM']
df = cowmast
Run Code Online (Sandbox Code Playgroud)
输出
print(df)
Cow Lact DIM xmast Lxmast
0 1 1 45 1 1
1 1 2 25 2 1
2 1 2 28 3 2
3 2 2 70 1 1
4 2 2 95 2 2
5 2 2 98 3 3
6 2 2 120 4 4
7 2 3 80 5 1
Run Code Online (Sandbox Code Playgroud)
现在,继续执行下面以粗体突出显示的要求的最后部分:
我想做的是重新启动每头牛(奶牛)泌乳期(Lact)的计数,并且仅当行之间的天数(DIM)超过 7 时才增加计数。
我们可以这样做:
为了使代码更具可读性,我们为目前的代码定义 2 个分组序列:
m_Cow = (df['Cow'] != df['Cow'].shift()).cumsum()
m_Lact = (df['Lact'] != df['Lact'].shift()).cumsum()
Run Code Online (Sandbox Code Playgroud)
然后,我们可以重写代码,以Lxmast更易读的格式进行设置,如下所示:
df['Lxmast'] = df.groupby([m_Cow, m_Lact]).cumcount()+1
Run Code Online (Sandbox Code Playgroud)
现在,转向这里的主要作品。假设我们Adjusted为其创建另一个新列:
df['Adjusted'] = (df.groupby([m_Cow, m_Lact])
['DIM'].diff().abs().gt(7)
.groupby([m_Cow, m_Lact])
.cumsum()+1
)
Run Code Online (Sandbox Code Playgroud)
结果:
print(df)
Cow Lact DIM xmast Lxmast Adjusted
0 1 1 45 1 1 1
1 1 2 25 2 1 1
2 1 2 28 3 2 1
3 2 2 70 1 1 1
4 2 2 95 2 2 2
5 2 2 98 3 3 2
6 2 2 120 4 4 3
7 2 3 80 5 1 1
Run Code Online (Sandbox Code Playgroud)
这里,在之后df.groupby([m_Cow, m_Lact]),我们取列DIM并检查每一行与前一行的差异.diff()并取绝对值by ,然后在代码片段中.abs()检查它是否> 7 。然后,我们再次按相同的分组进行分组,因为第三个条件位于前两个条件的分组内。我们在第三个条件上使用最后一步,以便只有当第三个条件为真时我们才会增加计数。.gt(7)['DIM'].diff().abs().gt(7).groupby([m_Cow, m_Lact]).cumsum()
如果您只想在增加> 7(例如70到78)时增加计数DIM并排除减少> 7 (不是从78到70) 的情况,您可以删除.abs()上面代码中的部分:
df['Adjusted'] = (df.groupby([m_Cow, m_Lact])
['DIM'].diff().gt(7)
.groupby([m_Cow, m_Lact])
.cumsum()+1
)
Run Code Online (Sandbox Code Playgroud)
由于您的示例数据具有主要分组键Cow并且Lact在某种程度上已经按排序顺序排列,因此有机会进一步简化代码。
与引用问题的样本数据不同,其中:
col count
0 B 1
1 B 2
2 A 1 # Value does not match previous row => reset counter to 1
3 A 2
4 A 3
5 B 1 # Value does not match previous row => reset counter to 1
Run Code Online (Sandbox Code Playgroud)
B在这里,最后一行中的最后一个与B其他的分开,并且需要将计数重置为 1,而不是从前count2 个中的最后一个继续B(变为 3)。因此,分组需要将当前行与前一行进行比较以获得正确的分组。否则,当我们在处理过程中使用.groupby()和 的值B分组在一起时,count最后一个条目的值可能无法正确重置为 1。
如果您的主分组键的数据在数据构建期间Cow已经Lact自然排序,或者已经按照如下指令排序:
df = df.sort_values(['Cow', 'Lact'])
Run Code Online (Sandbox Code Playgroud)
然后,我们可以简化我们的代码,如下所示:
(当数据已按 [ Cow, Lact] 排序时):
df['xmast'] = df.groupby('Cow').cumcount()+1
df['Lxmast'] = df.groupby(['Cow', 'Lact']).cumcount()+1
df['Adjusted'] = (df.groupby(['Cow', 'Lact'])
['DIM'].diff().abs().gt(7)
.groupby([df['Cow'], df['Lact']])
.cumsum()+1
)
Run Code Online (Sandbox Code Playgroud)
3 列中的结果和输出值相同xmast,Lxmast并且 Adjusted
| 归档时间: |
|
| 查看次数: |
1103 次 |
| 最近记录: |