在熊猫数据框中有效过滤

时间:2019-01-04 21:09:44

标签: python python-3.x pandas dataframe

我有一个包含3列的数据框-A,B和项目。 A和B包含给班级中每个学生的ID,并且项目是他们一起完成的#of个项目。数据位于A列和B列的级别

A |  B | projects
S2   S3    5
S2   S4    2
S5   S2    1
S5   S4    1

请注意,学生可以出现在输入数据集中的任何列中(S2在cola和colB中) 现在,我需要找到一个学生与其他所有学生一起完成的项目数量。数据框应该看起来像这样

id_ | StudentB | projects
S2     S3          5
S2     S5          1
S2     S4          2
S3     S2          5
S4     S2          2
S4     S5          1
S5     S4          1

现在,如果我过滤特定学生ID的ID_列,则所有相关ID都应列在StudentB列中

我的解决方案(“ all_student_id”是所有可能ID的不同列表)-

final_df = pd.DataFrame(columns = ['id_', 'studentB','projects'])
for id_ in all_student_id:
    data_ = data[(data['A']== id_) | (data['B']== id_)] 

    a = data_[['A','projects']].rename(columns= {'A':'studentB'})
    b = data_[['B','projects']].rename(columns= {'B':'studentB'})

    a_b_concat = pd.concat([a,b], axis=0)
    formatted = a_b_concat[a_b_concat['studentB']!=id_]

    temp = pd.DataFrame({'id_':[id_]*formatted.shape[0]
                        })
    temp = pd.concat([temp, formatted.reset_index(drop = True)], axis = 1)

    final_df= pd.concat([final_df, temp])

虽然我能够对所有不同的学生ID使用for循环来实现此目的,但我相信这不是可伸缩的解决方案,因为输入数据集可能非常庞大(3000万行)。

在优化此解决方案方面的任何帮助将不胜感激

3 个答案:

答案 0 :(得分:1)

您可以这样做:

# sort the students - create a new data frame
df1 = df[['A','B']]
df1.values.sort(axis=1)
df1['projects'] = df['projects']

# now groupb
df1.groupby(['A','B'])['projects'].sum().reset_index()

    A   B   projects
0   S2  S3  5
1   S2  S4  2
2   S2  S5  1
3   S4  S5  1

答案 1 :(得分:0)

这项工作可以吗?

因此,请让我知道此示例是否按您想要的方式工作:

m=pd.DataFrame({'A':("S2","S2","S5","S5",'S2'),'B':("S3","S4","S2","S4",'S5'), 'projects':(5,2,1,1,6)})

这有点像:

    A   B  projects
0  S2  S3         5
1  S2  S4         2
2  S5  S2         1
3  S5  S4         1
4  S2  S5         6

现在我想假设的是,如果您想让S2和S5一起出现在第1列或第2列中,那么它们应该算作反之亦然。出于这种自由,我进行了一些分析并得出以下结论:

f=np.sort(m.iloc[:,0:2].values)
pd.concat((pd.DataFrame(f),m[['projects']]),axis=1).groupby([0,1])['projects'].sum()

我得到的输出是:

0   1 
S2  S3    5
    S4    2
    S5    7
S4  S5    1

将列重命名为0和1,可以通过set_axis对其进行更改。要点是,这是您想要的吗? S2和S5,无论顺序如何,都将其总和作为输出?

答案 2 :(得分:0)

其他答案建议使用groupby,但从您期望的输出来看,我不同意这是您正在寻找的东西。似乎您只是希望将来自B->A的关系也包含为来自A->B的关系。这是一个微不足道的操作,可以通过堆叠列AB的反向版本来完成


a = df.values
b = a[:, [1,0,2]].copy()

d = pd.DataFrame(np.vstack((a, b)), columns=['id_', 'StudentB', 'projects'])

  id_ StudentB projects
0  S2       S3        5
1  S2       S4        2
2  S5       S2        1
3  S5       S4        1
4  S3       S2        5
5  S4       S2        2
6  S2       S5        1
7  S4       S5        1

现在,您可以只使用id_列查找任何学生,尽管我建议在此处使用pivot以获得更好的数据结构:

lookp = d.pivot('id_', 'StudentB', 'projects')

StudentB   S2   S3   S4   S5
id_
S2        NaN    5    2    1
S3          5  NaN  NaN  NaN
S4          2  NaN  NaN    1
S5          1  NaN    1  NaN

这为您提供了一种查找学生关系的简便方法,其中NaN表示两个学生没有一起从事任何项目。

>>> lookp.loc['S2', 'S3']
5
>>> lookp.loc['S3', 'S5']
nan