【问题标题】:Summing values from pandas dataframe columns depending on row index value根据行索引值对 pandas 数据框列的值求和
【发布时间】:2016-11-14 07:25:06
【问题描述】:

我有一个 NxN 数据框。每行对应于作为其索引给出的某个 url(没有“http://”)。每列也表示带有布尔值的 url,指示此页面(行索引)是否链接到该页面(列名)。 url 在索引和列中是相同的。

In [1]: import pandas as pd

In [2]: from pandas import DataFrame

In [3]:  df = DataFrame({'domain1.com/url1':[True,False,False,True,False],'domain2.com/url2':[False,True,False,True,True],'domain1.com/url3':[False,False,False,True,False],'domain3.com/url4':[False,True,False,True,False],'domain2.com/url5':[False,True,False,True,True]}, index=['domain1.com/url1','domain2.com/url2','domain1.com/url3','domain3.com/url4','domain2.com/url5'])

In [4]: df
Out[4]: 
                 domain1.com/url1 domain1.com/url3 domain2.com/url2  \
domain1.com/url1             True            False            False   
domain2.com/url2            False            False             True   
domain1.com/url3            False            False            False   
domain3.com/url4             True             True             True   
domain2.com/url5            False            False             True   

                 domain2.com/url5 domain3.com/url4  
domain1.com/url1            False            False  
domain2.com/url2             True             True  
domain1.com/url3            False            False  
domain3.com/url4             True             True  
domain2.com/url5             True            False  

例如,现在我可以计算每个 url 的传入和传出链接:

In [5]: in_links_count = df.sum(axis=0)

In [6]: in_links_count
Out[6]: 
domain1.com/url1    2
domain1.com/url3    1
domain2.com/url2    3
domain2.com/url5    3
domain3.com/url4    2
dtype: int64

In [7]: out_links_count = df.sum(axis=1)

In [8]: out_links_count
Out[8]: 
domain1.com/url1    1
domain2.com/url2    3
domain1.com/url3    0
domain3.com/url4    5
domain2.com/url5    2
dtype: int64

到目前为止一切顺利。但是,如果我只想计算 other 域的传入和传出链接怎么办?我想我需要以某种方式逐行过滤掉列。我尝试了诸如转置数据框(以排除列)和过滤之类的方法,但失败了:

In [9]: df_t = df.T

In [10]: df_t[ filter(lambda x: x.split('/')[0] != df_t.index.map(lambda x: x.split('/')[0]), list(df_t)) ].sum(axis=0)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-10-279439127551> in <module>()
----> 1 df_t[ filter(lambda x: x.split('/')[0] != df_t.index.map(lambda x: x.split('/')[0]), list(df_t)) ].sum(axis=0)

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

有什么想法吗,伙计们?

更新:

@piRSquared 提供了一种解决方案,该解决方案通过分层索引(stack()、index.to_series()、轴之间的差异、缺失数据的“假”值 - 见下文)生成第二个数据帧;这适用于中等大小的数据。但是,对于一个大的 NxN 数据帧(1000x1000),这肯定是矫枉过正。是否有其他方法,也许利用就地过滤/映射?

【问题讨论】:

  • 但是如果我只想计算其他域的传入和传出链接怎么办? - 能否介绍一下这一点
  • 我的意思是指向与正在考虑的 url 域不同的域的链接

标签: python pandas filtering


【解决方案1】:

不是很熊猫,但仍然......可以迭代元素

In [40]: def same_domain(url1, url2):
    return url1.split('/')[0] == url2.split('/')[0]


In [41]: def clear_inner_links(df):
for r in df.index:
    for c in df.columns:
        if(same_domain(r,c)):
            df.loc[r,c] = False
return df

那么就

df1.sum(0)
df1.sum(1)

基准测试:

In [35]: new_df.shape
Out[35]: (500, 500)

In [36]: %timeit clear_inner_links(new_df)
1 loop, best of 3: 956 ms per loop

更多熊猫方式:

In [102]: def same_domain(url1, url2):
   .....:         return url1.split('/')[0] == url2.split('/')[0]
   .....: 

In [103]: def apply_criterion(s):
   .....:         s[s.index.map(lambda x: same_domain(x,s.name))] = False
   .....:     

In [104]: def clear_inner_links2(df):
   .....:         df.apply(apply_criterion, axis=0)
   .....:         return df
   .....: 

In [105]: new_df.shape
Out[105]: (500, 500)

In [106]: %timeit clear_inner_links2(new_df)
1 loop, best of 3: 929 ms per loop

对于 1000 多个数据帧,第二个解决方案显示出比第一个解决方案(或 piRSquared 慢约 50 倍)更好的性能。

【讨论】:

  • 这正是我想要的。
【解决方案2】:

创建一系列索引/列对。过滤掉相同域的域,然后用False 填回。然后将axis1和与axis0相加。

def domain(x):
    return x.str.extract(r'([^/]+)', expand=False)

dfi = df.stack().index.to_series().apply(lambda x: pd.Series(x, ['u1', 'u2']))
keep = domain(dfi.u1) != domain(dfi.u2)
df1 = df.stack().ix[keep].unstack().fillna(False)

df1.sum(0) + df1.sum(1)

domain1.com/url1    1
domain1.com/url3    1
domain2.com/url2    2
domain2.com/url5    1
domain3.com/url4    5
dtype: int64

【讨论】:

  • 感谢您提出一个好而清晰的想法!你使用的是什么版本的熊猫 - 我得到 extract() got an unexpected keyword argument 'expand' and AttributeError: 'Series' object has no attribute 'extract' in 0.17
  • 0.18.1。只需删除该论点。我放它是因为如果我不这样做,我会收到警告。你暂时不需要它。
  • 在家里用大 df 尝试过,不幸的是,stack().index.to_series() 确实加热了一个非常强大的笔记本,也许除了在过程中间创建巨大的系列之外还有其他变体?
猜你喜欢
  • 1970-01-01
  • 2019-12-04
  • 2019-11-07
  • 1970-01-01
  • 2013-12-15
  • 2019-08-10
  • 2016-02-19
  • 2020-03-05
  • 2016-10-06
相关资源
最近更新 更多