所以我有这个pandas.Dataframe
C1 C2 C3 C4 C5 Start End C8
A 1 - - - 1 4 -
A 2 - - - 6 10 -
A 3 - - - 11 14 -
A 4 - - - 15 19 -
其中 - 是对象,Start是初始坐标,end是每个元素的最终坐标。
我定义了这个函数来计算表中所有区间的并集,在本例中它应该总和为[1,19] - {5}(基本上是一个包含所有包含元素的numpy数组)。
def coverage(table):
#return a dataframe with the coverage of each individual peptide in a protein
interval = (table.apply(lambda row : range(int(row['Start']),int(row['End'])+1),axis=1))]
#if there is only one peptide, return the range between its start and end positions
if len(table) == 1: return asarray(range(int(table['Start']),int(table['End'])+1))
#if there are more, unite all the intervals
if len(table) > 1:
return reduce(union1d,(list(interval)))
所以我将该函数迭代地应用于几个DataFrame(第一个是A,然后是B,C等)。问题是,对于某些表,这会失败并消除此错误:
Traceback (most recent call last):
File "At_coverage.py", line 37, in <module>
covdir[prot] = coverage(data)
File "At_coverage.py", line 21, in coverage
interval = (table.apply(lambda row : range(int(row['Start']),int(row['End'])+1),axis=1))
File "/usr/lib/python2.7/dist-packages/pandas/core/frame.py", line 3312, in apply
return self._apply_standard(f, axis, reduce=reduce)
File "/usr/lib/python2.7/dist-packages/pandas/core/frame.py", line 3417, in _apply_standard
result = self._constructor(data=results, index=index)
File "/usr/lib/python2.7/dist-packages/pandas/core/frame.py", line 201, in __init__
mgr = self._init_dict(data, index, columns, dtype=dtype)
File "/usr/lib/python2.7/dist-packages/pandas/core/frame.py", line 323, in _init_dict
dtype=dtype)
File "/usr/lib/python2.7/dist-packages/pandas/core/frame.py", line 4473, in _arrays_to_mgr
return create_block_manager_from_arrays(arrays, arr_names, axes)
File "/usr/lib/python2.7/dist-packages/pandas/core/internals.py", line 3760, in create_block_manager_from_arrays
construction_error(len(arrays), arrays[0].shape[1:], axes, e)
File "/usr/lib/python2.7/dist-packages/pandas/core/internals.py", line 3732, in construction_error
passed,implied))
ValueError: Shape of passed values is (7,), indices imply (7, 8)
失败的DataFrame如下:
Protein Peptide \
11106 sp|Q75W54|EBM_ARATH GJDGFJK
11107 sp|Q75W54|EBM_ARATH GJDGFJK
11108 sp|Q75W54|EBM_ARATH JJDPHJVSTFFDDYKR
11109 sp|Q75W54|EBM_ARATH JJDPHJVSTFFDDYKR
11110 sp|Q75W54|EBM_ARATH JNGEPJFJR
11111 sp|Q75W54|EBM_ARATH JNGEPJFJR
11112 sp|Q75W54|EBM_ARATH JNGEPJFJR
Fraction Count \
11106 AT_indark_IEX_fraction_18a_20150422.uniprot-pr... 2
11107 AT_indark_IEX_fraction_21a_20150422.uniprot-pr... 2
11108 AT_indark_IEX_fraction_18a_20150422.uniprot-pr... 2
11109 AT_indark_IEX_fraction_19a_20150422.uniprot-pr... 1
11110 AT_indark_IEX_fraction_19a_20150422.uniprot-pr... 2
11111 AT_indark_IEX_fraction_22a_20150422.uniprot-pr... 2
11112 AT_indark_IEX_fraction_25a_20150422.uniprot-pr... 2
Sequence Start End Length
11106 MAEIGKTVLDFGWIAARSTEVDVNGVQLTTTNPPAISSESRWMEAA... 577 584 944
11107 MAEIGKTVLDFGWIAARSTEVDVNGVQLTTTNPPAISSESRWMEAA... 577 584 944
11108 MAEIGKTVLDFGWIAARSTEVDVNGVQLTTTNPPAISSESRWMEAA... 210 226 944
11109 MAEIGKTVLDFGWIAARSTEVDVNGVQLTTTNPPAISSESRWMEAA... 210 226 944
11110 MAEIGKTVLDFGWIAARSTEVDVNGVQLTTTNPPAISSESRWMEAA... 344 353 944
11111 MAEIGKTVLDFGWIAARSTEVDVNGVQLTTTNPPAISSESRWMEAA... 344 353 944
11112 MAEIGKTVLDFGWIAARSTEVDVNGVQLTTTNPPAISSESRWMEAA... 344 353 944
[7 rows x 8 columns]
为了使其工作,我用
替换了第三行 interval = (table.apply(lambda row : range(int(row['Start']),int(row['End'])+4),axis=1)).apply(lambda row: row[:-3])
我注意到它也适用于除+1之外的任何其他数字(尽管在其他一些数字上它会在循环中的另一个DataFrame崩溃。
所以这个解决方案多余而且愚蠢。我的假设是这个特定数据框中的行数匹配一些奇怪的参数(比如列数或类似的东西),这使得Pandas试图简化某些东西然后崩溃。
我制作了一个简化版的程序,它也适用于多个开始和结束:
def multicov(row):
intervals = []
for i in range(len(row['Start'])):
#print data
intervals.append((range(int(row['Start'][i]),int(row['End'][i])+1)))
return reduce(union1d,intervals)
dir = {'Start':[[1,7],[14]],
'End':[[5,10],[18]]}
df = DataFrame(dir,columns=['Start','End'])
print df
print df.apply(multicov,axis=1)
在这种情况下,它会泄露相同的错误
ValueError: Shape of passed values is (2,), indices imply (2, 2)
但有趣的是,如果我从函数返回两个元素(因此它匹配2,2),它表现良好。
return reduce(union1d,intervals),'foobar'
Start End
0 [1, 7] [5, 10]
1 [14] [18]
[2 rows x 2 columns]
0 ([1, 2, 3, 4, 7, 8, 9, 10], foobar)
1 ([14, 15, 16, 17, 18], foobar)
dtype: object
如果我将输出指定为列表,
return [reduce(union1d,intervals),'foobar']
它将以前的列名称与输出匹配!
Start End
0 [1, 7] [5, 10]
1 [14] [18]
[2 rows x 2 columns]
Start End
0 [1, 2, 3, 4, 7, 8, 9] foobar
1 [14, 15, 16, 17] foobar
[2 rows x 2 columns]
所以我假设错误与Pandas试图在我之前的DataFrame和输出中的一个之间强制兼容,但我很惊讶,对于大多数DataFrame来说它运行良好!
答案 0 :(得分:1)
方法apply(func)
遍历行(或cols)并将func
应用于每一行。然后将func
的结果放入新数据框或系列中。如果func
返回标量值(例如sum
那么),那么它就是一个系列。如果它返回一个数组,列表或系列,那么结果是一个维度框架,具体取决于该数组的长度。
在您的代码中,func
返回不同长度(间隔长度)的数组,这些数组不能放在一个帧中。因此错误。 (实际上,你得到的第一个错误可能是这样的:ValueError: could not broadcast input array from shape (5) into shape (9)
。)
该行
return reduce(union1d,intervals),'foobar'
返回一个元组,因此apply
的结果是一个系列。并且
return [reduce(union1d,intervals),'foobar']
返回长度为2的列表。因此,您将获得n x 2
数据框。尺寸与输入数据框一致,因此pandas假设您想要修改原始框架的单元格(类似于应用lambda x: 2*x
)并保留列名称。
可能有效的解决方案是将您的函数中的range(x, y)
更改为tuple(range(x, y))
。但它既没有效率也没有pythonic。更好的方法是用行显式循环替换apply
,例如:
def coverage(table):
intervals = []
for row in table.itertuples():
intervals += list(range(row.Start, row.End + 1))
return np.unique(intervals)