计算多列中子字符串的频率,获得肯定结果的列名

时间:2019-05-22 08:22:23

标签: python pandas

我在pandas数据框中有多个患者的测序数据,每个患者都有一列基因型(字符串格式为x / x:xxx:xxxx),患者数量会有所不同... 我需要对这些信息做两件事,

  1. 计算每一行的每个基因型列中子字符串的频率,例如三位基因型患者:0/1:xxx:xxxx 0/1:xxxx:xxxx 1/1:xxxx:xxxx:xxxx,我需要将0/1行插入新列df ['freq_01' ],则df ['freq_11']等行中的1/1是多少次。

  2. 我需要为上一步中的每个基因型创建一个带有样品名称(列名称)的新列,例如: df ['samples_01] =“ S1,S2,S4”(每行)

我能够通过遍历每一行来做到这一点,但我想知道是否有可能通过熊猫来更有效地做到这一点。

原始数据(在我的文件中大约有10万行):

| id | S1      | S2      | S3      | S4      |
|----|---------|---------|---------|---------|
| 1  | 1/1:5:5 | 0/1:3:3 | 0/1:2:2 | 0/1:7:6 |
| 2  | 0/1:3:3 | 1/1:5:5 | 1/1:5:5 | 0       |
| 3  | 0/1:3:3 | 0       | 0/1:3:3 | 1/1:5:5 |

结果应如何显示:

| id | S1      | S2      | S3      | S4      | freq_01 | samples_01 |
|----|---------|---------|---------|---------|---------|------------|
| 1  | 1/1:5:5 | 0/1:3:3 | 0/1:2:2 | 0/1:7:6 | 3       | S2,S3,S4   |
| 2  | 0/1:3:3 | 1/1:5:5 | 1/1:5:5 | 0       | 1       | S1         |
| 3  | 0/1:3:3 | 0       | 0/1:3:3 | 1/1:5:5 | 2       | S1,S3      |

3 个答案:

答案 0 :(得分:1)

您可以简单使用:

'$1$2'

哪个将计算S1列中的子字符串“:”

df['S1'].str.count(':')

对于 id S1 S2 S3 S4 freq_01 samples_01 0 1 1/1:5:5 0/1:3:3 0/1:2:2 0/1:7:6 3 S2,S3,S4 1 2 0/1:3:3 1/1:5:5 1/1:5:5 0 1 S1 2 3 0/1:3:3 0 0/1:3:3 1/1:5:5 2 S1,S3 0 2 1 2 2 2 Name: S1, dtype: int64 列以逗号分隔的元素计数,请使用以下方法:

samples_01

答案 1 :(得分:0)

Pandas与列配合使用效果更好,所以我假设初始数据接近于您所显示的结果:

df = pd.DataFrame({1: {'S1': '1/1:5:5', 'S2': '0/1:3:3', 'S3': '0/1:2:2', 'S4': '0/1:7:6'},
                   2: {'S1': '0/1:3:3', 'S2': '1/1:5:5', 'S3': '1/1:5:5', 'S4': '0'},
                   3: {'S1': '0/1:3:3', 'S2': '0', 'S3': '0/1:3:3', 'S4': '1/1:5:5'}}
                  ).rename_axis('id', axis=1)

它打印为:

id        1        2        3
S1  1/1:5:5  0/1:3:3  0/1:3:3
S2  0/1:3:3  1/1:5:5        0
S3  0/1:2:2  1/1:5:5  0/1:3:3
S4  0/1:7:6        0  1/1:5:5

然后针对要处理的每个,我将构建一个临时数据框以了解该值是否存在。例如'0/1'

temp = df.apply(lambda x: x.str.match('0/1'))

给予:

id      1      2      3
S1  False   True   True
S2   True  False  False
S3   True  False   True
S4   True  False  False

然后可以很容易地找到频率和样本列表:

freq0_1 = temp.agg(lambda x: x[x].count()).rename('freq_01')
samples0_1 = temp.agg(lambda x: list(x[x].index)).rename('samples0_1')

如果要处理多个可能的值,这仍然很简单:

series = []
for value, ident in [('0/1', '01'), ('1/1', '11')]:
    temp = df.apply(lambda x: x.str.match(value))
    freq = temp.agg(lambda x: x[x].count()).rename('freq_' + ident)
    samples = temp.agg(lambda x: list(x[x].index)).rename('samples_' + ident)
    series.extend([freq, samples])

print(pd.concat(series, axis=1))

给予:

   freq_01    samples_01 freq_11 samples_11
id                                         
1        3  [S2, S3, S4]       1       [S1]
2        1          [S1]       2   [S2, S3]
3        2      [S1, S3]       1       [S4]

答案 2 :(得分:0)

您可以使用Series.str.startswith()将数据帧转换为所有布尔值的数组,然后计算频率并查找每行上具有True值的列名称。下面是示例代码:

#get a list of columns names required in calculation
cols = df.filter(like='S').columns
#Index(['S1', 'S2', 'S3', 'S4'], dtype='object')

# set up an array with True/False using Series.str.startswith
arr_01 = np.array([ df[c].str.startswith('0/1:') for c in cols ]).T
print(arr_01)
#array([[False,  True,  True,  True],
#       [ True, False, False, False],
#       [ True, False,  True, False]])

# count the True value on row
df['freq_01'] = np.sum(arr_01, axis=1)

# retrieve column names with True values only
df['samples_01'] = [ ','.join(filter(len, x)) for x in np.multiply(arr_01, np.array(cols)) ]
print(df)
#   id       S1       S2       S3       S4  freq_01  samples_01
#0   1  1/1:5:5  0/1:3:3  0/1:2:2  0/1:7:6        3    S2,S3,S4
#1   2  0/1:3:3  1/1:5:5  1/1:5:5        0        1          S1
#2   3  0/1:3:3        0  0/1:3:3  1/1:5:5        2       S1,S3

要处理更多情况,只需使用 for 循环:

cols = df.filter(like='S').columns

for t in [ '01', '11' ]:
    subt = t[0] + '/' + t[1] + ':'
    arr_t = np.array([ df[c].str.startswith(subt) for c in cols ]).T
    df['freq_{}'.format(t)] = np.sum(arr_t, axis=1)
    df['samples_{}'.format(t)] = [ ','.join(filter(len, x)) for x in np.multiply(arr_t, np.array(cols)) ]

print(df)
#   id       S1       S2       S3       S4  freq_01  freq_11  samples_01 samples_11
#0   1  1/1:5:5  0/1:3:3  0/1:2:2  0/1:7:6        3        1    S2,S3,S4         S1
#1   2  0/1:3:3  1/1:5:5  1/1:5:5        0        1        2          S1      S2,S3
#2   3  0/1:3:3        0  0/1:3:3  1/1:5:5        2        1       S1,S3         S4