2013-02-21 68 views
5

我有以下数据框,其中显示了从Item1到Item 2的移动次数。例如,从A到B有2个从A到C的转换,1从C到A计算数据框中特定列的成对差异


Item1 Item2 Moves 
    1 A  B  1 
    2 A  C  2 
    3 B  D  3 
    4 C  A  1 
    5 C  B  5 
    6 D  B  4 
    7 D  C  1 

我想计算两个项之间的差,因此一个新构造的数据帧将是以下

Item1 Item2 Moves 
    1 A  B  1 
    2 A  C  1 
    3 B  D  -1 
    4 C  B  5 
    5 D  C  1 

有没有人有任何想法如何做到这一点使用熊猫?我想我需要对前两栏进行索引,但我在熊猫中颇为新颖,而且我面临很多困难。 感谢

编辑 不能有任何重复pairs.For例如你不能看到两次A-> B(当然你可以看到的B-> A)

+0

重要的是你首先看到的过渡方向是否被保留,或者是否有'B C -5'的行是可以接受的? – DSM 2013-02-21 16:27:29

+0

这不是那么重要,但我想它是一个优先选择删除第一次遇到两个转换还是第二个转换的问题。 – BigScratch 2013-02-21 16:43:50

回答

3

我敢肯定有人可以简化这一行到更少的行,但我已经离开它很长时间来澄清发生了什么。简而言之,根据'Item1'是否比'Item2'更早,将数据框分成两部分。然后翻转'项目1'和'项目2',否定'移动'为一块。将它们粘在一起并使用groupby函数来聚合行。

>>> df 
    Item1 Item2 Moves 
0  A  B  1 
1  A  C  2 
2  B  D  3 
3  C  A  1 
4  C  B  5 
5  D  B  4 
6  D  C  1 
>>> swapidx = df['Item1'] < df['Item2'] 
>>> df1 = df[swapidx] 
>>> df2 = df[swapidx^True] 
>>> df1 
    Item1 Item2 Moves 
0  A  B  1 
1  A  C  2 
2  B  D  3 
>>> df2 
    Item1 Item2 Moves 
3  C  A  1 
4  C  B  5 
5  D  B  4 
6  D  C  1 
>>> df2[['Item1', 'Item2']] = df2[['Item2', 'Item1']] 
>>> df2['Moves'] = df2['Moves']*-1 
>>> df2 
    Item1 Item2 Moves 
3  A  C  -1 
4  B  C  -5 
5  B  D  -4 
6  C  D  -1 
>>> df3 = df1.append(df2) 
>>> df3.groupby(['Item1', 'Item2'], as_index=False).sum() 
    Item1 Item2 Moves 
0  A  B  1 
1  A  C  1 
2  B  C  -5 
3  B  D  -1 
4  C  D  -1 
+0

谢谢!这个答案对我来说似乎相当优雅 - 我已经设法使用列表来做这件事,但是我正在循环查看每一对的字典以找到相反的对,效率相当低 – BigScratch 2013-02-22 10:14:37

+0

您的建议编辑是一个很好的编辑。我不确定为什么会被别人拒绝,但在被3人拒绝后,我无法“批准”它。我做了编辑。 – 2013-02-22 17:34:48

1

下面是做这件事:

首先创建一个只包含Item1和Item2的字符串的行。

In [11]: df['Items'] = df.apply(lambda row: row['Item1'] + row['Item2'], axis=1) 

In [12]: df 
Out[12]: 
    Item1 Item2 Moves Items 
1  A  B  1 AB 
2  A  C  2 AC 
3  B  D  3 BD 
4  C  A  1 CA 
5  C  B  5 CB 
6  D  B  4 DB 
7  D  C  1 DC 

,如果Items是(字母)顺序离开它,否则切换后,否定Moves

In [13]: df[['Items','Moves']] = df.apply(lambda row: (row[['Items', 'Moves']]) 
                 if row['Items'][0] <= row['Items'][1] 
                 else (row['Items'][::-1], -row['Moves']), 
              axis=1) 

In [14]: df 
Out[14]: 
    Item1 Item2 Moves Items 
1  A  B  1 AB 
2  A  C  2 AC 
3  B  D  3 BD 
4  C  A  -1 AC 
5  C  B  -5 BC 
6  D  B  -4 BD 
7  D  C  -1 CD 

In [15]: g = df.groupby('Items') 

In [16]: g.sum() 
Out[16]: 
     Moves 
Items  
AB   1 
AC   1 
BC  -5 
BD  -1 
CD  -1 

这是大多数的方式,并可以为你就够了。

以获得所需的最终输出一个hackey方式可以是:

In [17]: df1 = g.first() # the first row in each group 

In [18]: df1.Moves = g.sum() 

In [19]: df2 = df1.reset_index(drop=True) 

In [20]: df2 
Out[20]: 
    Item1 Item2 Moves 
0  A  B  1 
1  A  C  1 
2  C  B  -5 
3  B  D  -1 
4  D  C  -1 

但是,请注意否定是不完全正确(对周围的人如DC而非CD的错误的方式):

In [21]: df2.Moves = df2.apply(lambda row: row['Moves'] 
              if row['Item1'] <= row['Item2'] 
              else -row['Moves'], 
           axis=1) 

In [22]: df2 
Out[22]: 
    Item1 Item2 Moves 
0  A  B  1 
1  A  C  1 
2  C  B  5 
3  B  D  -1 
4  D  C  1