用 1 替换各个列中的多个不同字符串,并用 pandas 中的计数改变一个新列
Replacing multiple different strings in various columns with 1 and mutating a new column with counts in pandas
我用 在 R 中做了这个,但切换到 Python 仍然没有看到好的答案。
我有一个包含 200 列不同字符串和数字的数据框。
示例:
Name Gender Disease1 Disease2 Disease3
Joe Male disease1 NA disease3
Ben Male NA disease2 NA
Chloe Female disease1 disease2 NA
如何将多个列中的不同 Disease
值转换为 1,然后改变一个新列,计算特定列(可能在列 22:65 中)1 的总数。
期望的输出
Name Gender Disease1 Disease2 Disease3 Total_diseases
Joe Male disease1 NA disease3 2
Ben Male NA disease2 NA 1
Chloe Female disease1 disease2 NA 2
我想要一个新列 Total_diseases
,其中汇总了所有文本值(现已转换为 1)。所以如果一个人有 10 种疾病,它就会出现在这个变异的列中。希望它能回答你的问题。
如果您只想要“Total_diseases”列,则无需对原始列进行任何更改:
df = df.set_index('id')
df['Total_diseases'] = df.nunique(axis=1)
如果最初有 'Disease' 以外的列:
cols = df.filter(like='Disease').columns
df['Total_diseases'] = df[cols].nunique(axis=1)
这只是为了将原始列更改为非 NA 值的一列:
cols = df.filter(like='Disease').columns
df[cols] = df[cols].where(df[cols].isna(), 1)
输出:
Disease1 Disease2 Disease3 Total_diseases
id
1 1 NaN 1 2
2 NaN 1 NaN 1
3 1 1 NaN 2
您可以在 id
上设置索引,然后使用 notna()
使这些条目不为空,并使用 .astype(int)
将它们更改为 1。然后在 axis=1
上按 .filter()
和 sum
过滤 Disease*
列以计算每行的计数:
df_out = df.set_index('id').notna().astype(int).reset_index()
df_out['Total_diseases'] = df_out.filter(like='Disease').sum(axis=1)
结果
print(df_out)
id Disease1 Disease2 Disease3 Total_diseases
0 1 1 0 1 2
1 2 0 1 0 1
2 3 1 1 0 2
编辑:
如果你想通过数字指定列的范围,你可以使用 .iloc
例如使用 df_out.iloc[:, 10:30]
并使用 df_out.iloc[:, 10:30].sum(axis=1)
对这些列求和。
编辑 2
根据更新的样本输入和期望的输出,你提到你想通过列号引用列的范围,而不是像上面的解决方案那样通过过滤类似的列标签(可能真正的疾病名称没有共同点pattern),可以使用.iloc
代替,如下:
df['Total_diseases'] = df.iloc[:, 2:5].notna().sum(axis=1)
结果
print(df)
Name Gender Disease1 Disease2 Disease3 Total_diseases
0 Joe Male disease1 NaN disease3 2
1 Ben Male NaN disease2 NaN 1
2 Chloe Female disease1 disease2 NaN 2
我用
我有一个包含 200 列不同字符串和数字的数据框。
示例:
Name Gender Disease1 Disease2 Disease3
Joe Male disease1 NA disease3
Ben Male NA disease2 NA
Chloe Female disease1 disease2 NA
如何将多个列中的不同 Disease
值转换为 1,然后改变一个新列,计算特定列(可能在列 22:65 中)1 的总数。
期望的输出
Name Gender Disease1 Disease2 Disease3 Total_diseases
Joe Male disease1 NA disease3 2
Ben Male NA disease2 NA 1
Chloe Female disease1 disease2 NA 2
我想要一个新列 Total_diseases
,其中汇总了所有文本值(现已转换为 1)。所以如果一个人有 10 种疾病,它就会出现在这个变异的列中。希望它能回答你的问题。
如果您只想要“Total_diseases”列,则无需对原始列进行任何更改:
df = df.set_index('id')
df['Total_diseases'] = df.nunique(axis=1)
如果最初有 'Disease' 以外的列:
cols = df.filter(like='Disease').columns
df['Total_diseases'] = df[cols].nunique(axis=1)
这只是为了将原始列更改为非 NA 值的一列:
cols = df.filter(like='Disease').columns
df[cols] = df[cols].where(df[cols].isna(), 1)
输出:
Disease1 Disease2 Disease3 Total_diseases
id
1 1 NaN 1 2
2 NaN 1 NaN 1
3 1 1 NaN 2
您可以在 id
上设置索引,然后使用 notna()
使这些条目不为空,并使用 .astype(int)
将它们更改为 1。然后在 axis=1
上按 .filter()
和 sum
过滤 Disease*
列以计算每行的计数:
df_out = df.set_index('id').notna().astype(int).reset_index()
df_out['Total_diseases'] = df_out.filter(like='Disease').sum(axis=1)
结果
print(df_out)
id Disease1 Disease2 Disease3 Total_diseases
0 1 1 0 1 2
1 2 0 1 0 1
2 3 1 1 0 2
编辑:
如果你想通过数字指定列的范围,你可以使用 .iloc
例如使用 df_out.iloc[:, 10:30]
并使用 df_out.iloc[:, 10:30].sum(axis=1)
对这些列求和。
编辑 2
根据更新的样本输入和期望的输出,你提到你想通过列号引用列的范围,而不是像上面的解决方案那样通过过滤类似的列标签(可能真正的疾病名称没有共同点pattern),可以使用.iloc
代替,如下:
df['Total_diseases'] = df.iloc[:, 2:5].notna().sum(axis=1)
结果
print(df)
Name Gender Disease1 Disease2 Disease3 Total_diseases
0 Joe Male disease1 NaN disease3 2
1 Ben Male NaN disease2 NaN 1
2 Chloe Female disease1 disease2 NaN 2