在列中统计项,还是为什么我的代码只统计字符串中的一个项?

后端开发 2026-07-08

这是对 昨天的问题 的延续。

我需要统计一个词(基因名)在一行中出现的次数(细菌样本)。我想看看有多少对重复拷贝的基因。

自昨天起,我决定把包含基因名的所有列合并成一列:

df3['AMRs'] = df3[df3.columns[0:]].apply(lambda x: ','.join(x.dropna().astype(str)),axis=1)

得到的列像这样:

0        fosX,vga(G)
1        fosX,vga(G)
2        fosX,vga(G)
3        fosX,vga(G)
4        fosX,vga(G)
            ...     
#number of samples/rows is 18022.

现在我试着统计每个基因:

y= {}  # dict
for X in genenames: 
    y[X] = df2["AMRs"].str.count(X)

for key in list(y.keys()):
    print(key, ':', y[key])

其中genenames是:

['fosX', 'vga(G)', 'emrC', 'tet(M)', ... , 'vga', 'vga=HMM'] #there are 100 genes

但没有为fosX和 vga(G) 得到1(或2),我得到的是:

{'fosX': 0        1
1        1
2        1
3        1
4        1
        ..
'vga(G)': 0        0
1        0
2        0
3        0
4        0
        ..

如果你查看那一列,两个基因都应该有1,但vga(G) 没有被计入。

奇怪的是,在列表末尾的“vga”是除了fosX之外唯一得到1 的基因,但我知道这些样本并不拥有这个基因。

我到底做错了什么?是正则表达式的问题吗?

解决方案

问题在于 ( )vga(G) 中具有特殊含义,而 .str.count()pandas 中使用的是正则表达式。

它可能需要将 ( 替换为 \(,将 ) 替换为 \),或者更简单地使用 re.escape()

y[X] = df["AMRs"].str.count( X.replace("(", r"\(").replace(")", r"\)") )

y[X] = df["AMRs"].str.count( re.escape(X) )

最终使用 apply(),配合标准的 str.count()

y[X] = df["AMRs"].apply(lambda text: text.count(X))

但正则表达式还有另一个问题:count("vga") 也会把 vga(G)/vga=HMM 计为 vga。它需要使用 ^vga$

使用 .eq().sum() 时,vga(G)/vga=HMM 会被当作与 vga 不同的元素。

而且不需要把所有列都转换成一个字符串。

for X in genenames:
    # no need to use `df[df.columns[0:]]`
    y[X] = df.eq(X).sum(axis=1)

顺便说一句:要在随机数据上测试,可以使用 random.choices()

import random
#random.seed(0)  # to have always the same random data

genenames = ["fosX", "vga(G)", "emrC", "tet(M)", "vga", "vga=HMM"]

cols = 20
rows = 5
df = pd.DataFrame([
    random.choices(genenames, k=cols)
    for _ in range(rows)
])

用于测试的最小可运行代码:

import pandas as pd
import io
import re
import random

random.seed(0)  # to have always the same random data

text = """
0,fosX,vga(G)
1,fosX,vga(G)
2,fosX,vga(G)
3,fosX,vga(G)
4,fosX,vga(G)
"""

genenames = ["fosX", "vga(G)", "emrC", "tet(M)", "vga", "vga=HMM"]

# --- create dataframe ---

df = pd.read_csv(io.StringIO(text), header=None)

cols = 5
rows = 3
df = pd.DataFrame([random.choices(genenames, k=cols) for _ in range(rows)])

print("--- df ---")
print(df)

# --- .str.count() ---

# no need to use `df[df.columns[0:]]`
df["AMRs"] = df.apply(
    lambda row: ",".join(row.dropna().astype(str)), axis=1
)

y = pd.DataFrame()

for X in genenames:
    # y[X] = df["AMRs"].str.count(X)
    # y[X] = df["AMRs"].str.count(X.replace("(", r"\(").replace(")", r"\)"))
    y[X] = df["AMRs"].str.count(re.escape(X))
    # y[X] = df["AMRs"].apply(lambda text: text.count(X))

print("--- .str.count() ---")
print(pd.DataFrame(y))

# --- .eq().sum() ---

y = pd.DataFrame()

for X in genenames:
    # no need to use `df[df.columns[0:]]`
    y[X] = df.eq(X).sum(axis=1)
    # y[X] = (df == X).sum(axis=1)


print("--- .eq().sum() ---")
print(y)

结果:

(列 vga.str.count().eq().sum() 的结果不同

--- df ---
         0       1       2       3       4
0  vga=HMM     vga    emrC  vga(G)  tet(M)
1     emrC     vga  vga(G)    emrC  tet(M)
2  vga=HMM  tet(M)  vga(G)     vga  tet(M)
--- .str.count() ---
   fosX  vga(G)  emrC  tet(M)  vga  vga=HMM
0     0       1     1       1    3        1
1     0       1     2       1    2        0
2     0       1     0       2    3        1
--- .eq().sum() ---
   fosX  vga(G)  emrC  tet(M)  vga  vga=HMM
0     0       1     1       1    1        1
1     0       1     2       1    1        0
2     0       1     0       2    1        1
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章