减少列中具有重复值的行并以不同方式汇总列的其余部分
Reduce rows that has a repeated values in a column and summarises rest of column in different ways
我有一个table这样的
import pandas as pd
data = [
['ACOT', '00001', '', '', 1.5, 20, 30],
['ACOT', '00002', '', '', 1.7, 20, 33],
['ACOT', '00003', '','NA_0001' ,1.4, 20, 40],
['PAN', '000090', 'canonical', '', 0.5, 10, 30],
['PAN', '000091', '', '', 0.4, 10, 30],
['TOM', '000080', 'canonical', '', 0.4, 10, 15],
['TOM', '000040', '', '', 1.7, 10, 300]
]
df = pd.DataFrame(data, columns=[
'Gene_name', 'Transcript_ID', 'canonical', 'mane', 'metrics','start','end'])
输出
Gene_name Transcript_ID canonical mane metrics start end
0 ACOT 00001 1.5 20 30
1 ACOT 00002 NA_0001 1.7 20 33
2 ACOT 00003 1.4 20 40
3 PAN 000090 canonical NA_00090 0.5 10 30
4 PAN 000091 0.4 10 30
5 TOM 000080 canonical 0.4 10 15
6 TOM 000040 1.7 10 300
我想要这个输出
Gene_name canonical mane metrics
0 ACOT No Yes 1.4-1.5
4 PAN Yes Yes 0.5-0.4
5 TOM Yes No 1.7-0.4
如果 mane/canonical 有值,则输入 yes 否则输入 no。创建指标列中找到的最高值和最低值之间的范围。
编辑:除了我的解决方案有效之外,我建议接受@965311532 的回答,因为它是“pandas-way”和“pythonic”的完美结合“ 代码。行数少得多,阅读起来也更好。
现在我的解决方案:
我不得不说您的示例代码没有提供准确的输出数据。例如查看 mane
列。而你想要的输出中的 metrics
列没有意义,因为 min-max 和 max-min 是混合的。
我的示例代码将此输出作为示例数据:
Gene_name canonical mane metrics
0 ACOT 1.5
1 ACOT 1.7
2 ACOT NA_0001 1.4
3 PAN canonical 0.5
4 PAN 0.4
5 TOM canonical 0.4
6 TOM 1.7
转换后的结果为
canonical mane metrics
Gene_name
ACOT No No 1.4-1.7
PAN Yes No 0.4-0.5
TOM Yes No 0.4-1.7
这就是解决代码。
#!/usr/bin/env python3
import pandas as pd
data = [
['ACOT', '00001', '', '', 1.5, 20, 30],
['ACOT', '00002', '', '', 1.7, 20, 33],
['ACOT', '00003', '','NA_0001' ,1.4, 20, 40],
['PAN', '000090', 'canonical', '', 0.5, 10, 30],
['PAN', '000091', '', '', 0.4, 10, 30],
['TOM', '000080', 'canonical', '', 0.4, 10, 15],
['TOM', '000040', '', '', 1.7, 10, 300]
]
df = pd.DataFrame(data, columns=[
'Gene_name', 'Transcript_ID', 'canonical', 'mane', 'metrics','start','end'])
# select only the columns we need
df = df.loc[:, ['Gene_name', 'canonical', 'mane', 'metrics']]
# helper function to used in apply()
def my_helper(group):
# temporary store the result as a dict
result = {}
# check for existance of the values
# it is easy because columnname, value and result column
# have the same "name"
for col in ['canonical', 'mane']:
result[col] = \
'Yes' if group[col].isin([col]).any() else 'No'
# I use string format here. Could also be a tuple,
# list, Series or what every you want.
result['metrics'] = '{}-{}'.format(group.metrics.min(),
group.metrics.max())
return pd.Series(result)
desired = df.groupby('Gene_name').apply(my_helper)
这里的重要部分是使用 groupby()
,然后在单独的辅助函数中进行计算和检查。
一些细节
关于那条线
def my_helper(group):
result = {'Gene_name': group.Gene_name.iloc[0]}
my_helper()
函数为您的每个 Gene_name
调用了三次。 group
参数是一个 DataFrame
。例如,在第一次调用中,它的内容如下所示:
Gene_name canonical mane metrics
0 ACOT 1.5
1 ACOT 1.7
2 ACOT NA_0001 1.4
为了收集结果,我在这里使用了 dict()
,因为它的键稍后用作结果数据框的列名。
关于这条线:
for col in ['canonical', 'mane']:
result[col] = \
'Yes' if group[col].isin([col]).any() else 'No'
这可能看起来有点连线,但恕我直言,它更像 pythonic。也可以这样多写几行
if group['canonical'].isin(['canonical']).any():
result['canonical'] = 'Yes'
else:
result['canonical'] = 'No'
# and the same for 'mane'
.isin(['canonical'])
部分为您提供布尔值列表。 .any()
return True
如果该列表中有最小值 True
,如果没有则 False
。
关于那条线
return pd.Series(result)
此处从 dict
创建了一个 Series
对象。从 my_helper()
的第一次调用开始,该系列看起来像这样:
Gene_name ACOT
canonical No
mane No
metrics 1.4-1.7
这是一个列表,其中每个项目都有自己的名称。完整的代码将生成三个这样的系列对象(每个 Gene_name
)。 apply()
确实将每个系列用作一行并将它们粘合在一起到一个新的数据框。
你可以这样做:
f = lambda x: "Yes" if x.any() else "No" # For canonical and mane
df = df.groupby('Gene_name').agg({'canonical': f, 'mane': f, 'metrics': ['min', 'max']})
# Rename columns
df.columns = ["canonical", "mane", "metrics_min", "metrics_max"]
输出:
canonical mane metrics_min metrics_max
Gene_name
ACOT No Yes 1.4 1.7
PAN Yes Yes 0.4 0.5
TOM Yes No 0.4 1.7
我有一个table这样的
import pandas as pd
data = [
['ACOT', '00001', '', '', 1.5, 20, 30],
['ACOT', '00002', '', '', 1.7, 20, 33],
['ACOT', '00003', '','NA_0001' ,1.4, 20, 40],
['PAN', '000090', 'canonical', '', 0.5, 10, 30],
['PAN', '000091', '', '', 0.4, 10, 30],
['TOM', '000080', 'canonical', '', 0.4, 10, 15],
['TOM', '000040', '', '', 1.7, 10, 300]
]
df = pd.DataFrame(data, columns=[
'Gene_name', 'Transcript_ID', 'canonical', 'mane', 'metrics','start','end'])
输出
Gene_name Transcript_ID canonical mane metrics start end
0 ACOT 00001 1.5 20 30
1 ACOT 00002 NA_0001 1.7 20 33
2 ACOT 00003 1.4 20 40
3 PAN 000090 canonical NA_00090 0.5 10 30
4 PAN 000091 0.4 10 30
5 TOM 000080 canonical 0.4 10 15
6 TOM 000040 1.7 10 300
我想要这个输出
Gene_name canonical mane metrics
0 ACOT No Yes 1.4-1.5
4 PAN Yes Yes 0.5-0.4
5 TOM Yes No 1.7-0.4
如果 mane/canonical 有值,则输入 yes 否则输入 no。创建指标列中找到的最高值和最低值之间的范围。
编辑:除了我的解决方案有效之外,我建议接受@965311532 的回答,因为它是“pandas-way”和“pythonic”的完美结合“ 代码。行数少得多,阅读起来也更好。
现在我的解决方案:
我不得不说您的示例代码没有提供准确的输出数据。例如查看 mane
列。而你想要的输出中的 metrics
列没有意义,因为 min-max 和 max-min 是混合的。
我的示例代码将此输出作为示例数据:
Gene_name canonical mane metrics
0 ACOT 1.5
1 ACOT 1.7
2 ACOT NA_0001 1.4
3 PAN canonical 0.5
4 PAN 0.4
5 TOM canonical 0.4
6 TOM 1.7
转换后的结果为
canonical mane metrics
Gene_name
ACOT No No 1.4-1.7
PAN Yes No 0.4-0.5
TOM Yes No 0.4-1.7
这就是解决代码。
#!/usr/bin/env python3
import pandas as pd
data = [
['ACOT', '00001', '', '', 1.5, 20, 30],
['ACOT', '00002', '', '', 1.7, 20, 33],
['ACOT', '00003', '','NA_0001' ,1.4, 20, 40],
['PAN', '000090', 'canonical', '', 0.5, 10, 30],
['PAN', '000091', '', '', 0.4, 10, 30],
['TOM', '000080', 'canonical', '', 0.4, 10, 15],
['TOM', '000040', '', '', 1.7, 10, 300]
]
df = pd.DataFrame(data, columns=[
'Gene_name', 'Transcript_ID', 'canonical', 'mane', 'metrics','start','end'])
# select only the columns we need
df = df.loc[:, ['Gene_name', 'canonical', 'mane', 'metrics']]
# helper function to used in apply()
def my_helper(group):
# temporary store the result as a dict
result = {}
# check for existance of the values
# it is easy because columnname, value and result column
# have the same "name"
for col in ['canonical', 'mane']:
result[col] = \
'Yes' if group[col].isin([col]).any() else 'No'
# I use string format here. Could also be a tuple,
# list, Series or what every you want.
result['metrics'] = '{}-{}'.format(group.metrics.min(),
group.metrics.max())
return pd.Series(result)
desired = df.groupby('Gene_name').apply(my_helper)
这里的重要部分是使用 groupby()
,然后在单独的辅助函数中进行计算和检查。
一些细节
关于那条线
def my_helper(group):
result = {'Gene_name': group.Gene_name.iloc[0]}
my_helper()
函数为您的每个 Gene_name
调用了三次。 group
参数是一个 DataFrame
。例如,在第一次调用中,它的内容如下所示:
Gene_name canonical mane metrics
0 ACOT 1.5
1 ACOT 1.7
2 ACOT NA_0001 1.4
为了收集结果,我在这里使用了 dict()
,因为它的键稍后用作结果数据框的列名。
关于这条线:
for col in ['canonical', 'mane']:
result[col] = \
'Yes' if group[col].isin([col]).any() else 'No'
这可能看起来有点连线,但恕我直言,它更像 pythonic。也可以这样多写几行
if group['canonical'].isin(['canonical']).any():
result['canonical'] = 'Yes'
else:
result['canonical'] = 'No'
# and the same for 'mane'
.isin(['canonical'])
部分为您提供布尔值列表。 .any()
return True
如果该列表中有最小值 True
,如果没有则 False
。
关于那条线
return pd.Series(result)
此处从 dict
创建了一个 Series
对象。从 my_helper()
的第一次调用开始,该系列看起来像这样:
Gene_name ACOT
canonical No
mane No
metrics 1.4-1.7
这是一个列表,其中每个项目都有自己的名称。完整的代码将生成三个这样的系列对象(每个 Gene_name
)。 apply()
确实将每个系列用作一行并将它们粘合在一起到一个新的数据框。
你可以这样做:
f = lambda x: "Yes" if x.any() else "No" # For canonical and mane
df = df.groupby('Gene_name').agg({'canonical': f, 'mane': f, 'metrics': ['min', 'max']})
# Rename columns
df.columns = ["canonical", "mane", "metrics_min", "metrics_max"]
输出:
canonical mane metrics_min metrics_max
Gene_name
ACOT No Yes 1.4 1.7
PAN Yes Yes 0.4 0.5
TOM Yes No 0.4 1.7