iterrows()和列来标识条目位置

2024-05-16 17:45:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一张考勤表。每行有一个名称,后面有53列,表示一年中的周数。当一个人出席时,该周的分数为1,否则为空。我总结了每个人的总出勤率,并制作了参观次数的柱状图。很多人一年参加不到6次。我想看看访问之间的时间跨度;每个至少访问2次的人在第一次访问和最后一次访问之间的间隔时间。下面是df.tail()的图像。索引1433提供了一个示例,该人员访问了两次,第一周一次,第46周一次,即45周。我试过for i,j in df.iterrows():,但没能让它工作,我不确定这是个好主意

enter image description here

非常感谢您的任何想法


Tags: in图像名称示例dffor人员次数
2条回答

使用first_valid_index&last_valid_index

Pandas具有第一个有效索引和最后一个有效索引,以获取非空位置的索引。您可以通过使用iloc将数据帧切片到52周来找到位置。在下面的示例中,我只将其切片15周(0到14)。要将索引获取为整数,请创建一个包含列名和位置的字典。用它减去差值,得到间隙

下面是执行此操作的代码

dcols = {'wk'+str(i+1):i+1 for i in range (53)}

df['start_pos'] = df.iloc[:,0:15].apply(lambda x: x.first_valid_index(), axis=1).replace(dcols)

df['end_pos'] = df.iloc[:,0:15].apply(lambda x: x.last_valid_index(), axis=1).replace(dcols)

df['visit_gap'] = df['end_pos'] - df['start_pos']
print (df[['start_pos','end_pos','visit_gap']])

如果只想检查访问次数为2或更多的行,只需将其添加到条件中即可

我以前共享的数据(数据见下文)的输出为:

   start_pos  end_pos  visit_gap
0          2       15         13
1          1       15         14
2          4       10          6

使用Apply和list

这里有一个解决方法。您可以在dataframe上使用apply函数,并对给定行的每一列进行迭代,以确定它是否为notnull。如果是,则捕获列#。这将为您提供所有不为null的列。一旦有了它,就更容易找到两个列索引之间的最大差异。这将为您提供两次访问之间的最大间隔

执行此操作的代码如下所示:

import pandas as pd
import numpy as np
pd.set_option('display.max_columns', None)
df = pd.DataFrame({'wk1': [np.nan,1,np.nan],       'wk2': [1,np.nan,np.nan], 
                   'wk3': [1,1,np.nan],            'wk4': [np.nan,np.nan,1], 
                   'wk5': [np.nan,np.nan,np.nan],  'wk6': [1,1,np.nan],
                   'wk7': [1,1,np.nan],            'wk8': [1,np.nan,np.nan],
                   'wk9': [np.nan,1,np.nan],       'wk10': [np.nan,np.nan,1],
                   'wk11': [np.nan,np.nan,np.nan], 'wk12': [1,1,np.nan],
                   'wk13': [np.nan,1,np.nan],      'wk14': [1,np.nan,np.nan],
                   'wk15': [1,1,np.nan],           'wk16': [np.nan,1,np.nan], 
                   'wk17': [1,np.nan,np.nan],      'wk18': [1,1,np.nan],
                   'wk19': [np.nan,1,np.nan],      'wk20': [1,np.nan,1],
                   'wk21': [1,1,np.nan], })

#print (df)
df['attendance'] = df.apply(lambda x: [i for i,c in enumerate(df.columns) if pd.notnull(x[c])], axis=1)
print (df)

以下是输出:

   wk1  wk2  wk3  wk4  wk5  wk6  wk7  wk8  wk9  wk10  wk11  wk12  wk13  wk14  \
0  NaN  1.0  1.0  NaN  NaN  1.0  1.0  1.0  NaN   NaN   NaN   1.0   NaN   1.0   
1  1.0  NaN  1.0  NaN  NaN  1.0  1.0  NaN  1.0   NaN   NaN   1.0   1.0   NaN   
2  NaN  NaN  NaN  1.0  NaN  NaN  NaN  NaN  NaN   1.0   NaN   NaN   NaN   NaN   

   wk15  wk16  wk17  wk18  wk19  wk20  wk21  \
0   1.0   NaN   1.0   1.0   NaN   1.0   1.0   
1   1.0   1.0   NaN   1.0   1.0   NaN   1.0   
2   NaN   NaN   NaN   NaN   NaN   1.0   NaN   

                                    attendance  
0  [1, 2, 5, 6, 7, 11, 13, 14, 16, 17, 19, 20]  
1  [0, 2, 5, 6, 8, 11, 12, 14, 15, 17, 18, 20]  
2                                   [3, 9, 19]  

最后一列现在有一些值,您可以通过这些值进行迭代,并了解他们参加会议的频率

如果您想要访问次数,只需执行以下操作:

df["Total_Visits"] = df.sum(axis=1)

由于最后一列是'Total_Visits',因此apply语句必须使用df.columns[:-1]

如果你想得到两次访问之间的最大间隔,那么你可以给出这个

df['max_gap'] = df.apply(lambda x: max(np.diff([i for i,c in enumerate(df.columns) if pd.notnull(x[c])])), axis=1)

其结果将是:

   max_gap                                   attendance
0        4  [1, 2, 5, 6, 7, 11, 13, 14, 16, 17, 19, 20]
1        3  [0, 2, 5, 6, 8, 11, 12, 14, 15, 17, 18, 20]
2       10                                   [3, 9, 19]
为了找到最大的差距,你还需要考虑“TooTraveAccess”栏。这样,你就可以知道是否有人在第10周后就再也没有来过。最大间隔为52周-10周。为此,我们需要包括“总访问量”。这就是为什么我们将遍历df.columns而不是df.columns[:-1]

我认为这很简单,也很容易理解

设置:

cols = [f'WK_{i}' for i in range(1,54)]
data = {cols[i]:np.random.randint(0,2,10) for i in range(len(cols))}
df = pd.DataFrame(data)
>>> df
   WK_1  WK_2  WK_3  WK_4  WK_5  WK_6  ...  WK_48  WK_49  WK_50  WK_51  WK_52  WK_53
0     1     1     0     0     0     0  ...      1      0      0      1      1      0
1     1     1     1     1     0     1  ...      1      0      0      1      0      0
2     1     0     0     1     1     1  ...      0      1      0      1      1      0
3     0     0     0     1     1     0  ...      1      1      0      0      1      1
4     1     1     1     1     0     1  ...      0      1      0      1      1      1
5     1     1     0     0     0     1  ...      0      0      1      0      0      1
6     0     1     1     0     1     0  ...      0      0      1      0      0      1
7     0     1     1     0     1     1  ...      0      0      1      1      1      0
8     0     0     1     1     0     0  ...      1      1      0      0      0      0
9     1     1     1     0     0     0  ...      0      1      1      0      0      1

[10 rows x 53 columns]

转换df以便于系列操作:

trans = df.T.reset_index()
>>> trans.head()
  index  0  1  2  3  4  5  6  7  8  9
0  WK_1  1  1  1  0  1  1  0  0  0  1
1  WK_2  1  1  0  0  1  1  1  1  0  1
2  WK_3  0  1  0  0  1  0  1  1  1  1
3  WK_4  0  1  1  1  1  0  0  0  1  0
4  WK_5  0  0  1  1  0  0  1  1  0  0

创建一系列所有出席人数:

df.weeks_attended = df.apply(lambda x: trans.loc[trans[x.name]==1].index.tolist(),axis=1)

最后,从最后一次出席人数中减去第一次出席人数,得到跨度:

df['span between visits'] = df.weeks_attended.apply(lambda x: x[-1]-x[0])

结果:

>>> df
   WK_1  WK_2  WK_3  WK_4  ...  WK_51  WK_52  WK_53  span between visits
0     1     0     0     1  ...      0      0      1                   52
1     1     0     0     1  ...      1      1      0                   51
2     1     0     0     0  ...      0      0      0                   45
3     1     0     0     1  ...      0      1      1                   52
4     0     0     1     0  ...      0      1      1                   50
5     1     0     1     1  ...      0      1      1                   52
6     0     1     0     1  ...      0      1      0                   50
7     0     0     0     1  ...      0      0      0                   42
8     1     0     1     1  ...      1      1      1                   52
9     0     0     1     1  ...      1      0      1                   50

相关问题 更多 >