Hi everyone! I wanted to share my process for remapping PPMI’s proteomic data from the original gene table to the most recent information from SomaLogic. This is based on guidance provided by @tiago.azevedo after his discussion with a SomaLogic representative, as described here: https://rcop.michaeljfox.org/t/best-way-to-match-somascan-sequence-ids-to-gene-ids/1663.
Motivation
The core issue is that PPMI originally used a custom panel based on v3 of the SomaScan assay, with a corresponding table mapping aptamer sequence IDs to genes. Since then, SomaLogic have refined their assay and understanding, and they provide a website for looking up the mapping between a sequence ID and gene. When you look up one of the original PPMI SomaScan sequence IDs in the new website, there are many discrepancies with the original mapping table. The guidance provided by SomaLogic is that the sequence IDs immutably refer to the same aptamer across all assay versions, and we should be using the most recent gene mapping, as it represents the state-of-the-art understanding of which proteins these aptamers truly bind to.
However, it was not immediately obvious to me how to remap the sequence IDs for highest accuracy! This post documents my process, in the hope that it can be useful to others, and/or I can get some feedback to improve it further!
Datasets required for mapping
First up, I needed a bulk download of the new SomaLogic sequence ID to gene mapping. I found this from the SomaLogic GitHub page, where there is a tab-separated file of sequence ID, Entrez ID, and target description.
Next, I needed a mapping from Entrez ID to Gene Name, which I got from BioMart by selecting “Ensembl Genes 116” then “Human genes”, and selecting the “Attributes” of:
- “Gene stable ID”
- “Gene name”
- “NCBI gene (formerly Entrezgene) ID”
- “NCBI gene (formerly Entrezgene) accession”
(Note: this takes care of the problem where some IDs map to mouse genes. I’m only including human genes in the set of possible genes to map to!)
Finally I needed the list of PPMI sequence IDs. I specifically work with Project 151 data, which is CSF proteomics for all participants at the baseline visit. The PPMI Project 151 file from LONI has a second sheet called “Lookup”, with columns for LABEL, GENE_SYMBOL, and TESTNAME, among others.
Creating a mapping table
The first step was to join the first two datasets using Entrez ID. Using Pandas in Python, this looks like:
df1 = pd.read_csv("SomaScan_11k_seqIDs_EntrezIDs_targFullNames.tsv",
sep="\t", header=None,
names=["Soma ID", "Entrez", "description"])
df2 = pd.read_csv("mart_export.csv")
df2 = df2[df2["NCBI gene (formerly Entrezgene) ID"].notnull()]
df2["NCBI gene (formerly Entrezgene) ID"] = df2["NCBI gene (formerly Entrezgene) ID"].astype(int)
df1 = df1.merge(df2, how="left", left_on="Entrez",
right_on="NCBI gene (formerly Entrezgene) ID").drop(
columns="NCBI gene (formerly Entrezgene) ID")
Now we have SomaLogic sequence ID mapped to human gene name, where such a mapping exists through Entrez ID. Most of these genes have a “Gene name”, but there are some with a null value for “Gene name” which have a name in the “NCBI gene (formerly Entrezgene) accession” column. Next I create a “New gene name” column which takes the value from “Gene name” if it exists, but falls back to the Entrezgene accession if “Gene name” is null.
df1["New gene name"] = df1.apply(
lambda row: row["Gene name"] if not pd.isnull(row["Gene name"]) \
else row["NCBI gene (formerly Entrezgene) accession"], axis=1)
print(df1["New gene name"].nunique(), df1["New gene name"].isnull().sum())
This shows that we now have 9550 unique gene names, and 59 that are still null. Unfortunately there is not much I can do with those nulls, so they end up removed from the final mapping.
Finally, one sequence ID can legitimately map to multiple genes. One example is a protein complex comprised of components assembled from multiple genes. The original PPMI table handles this by listing multiple genes separated by semi-colon, so I did the same here. The logic is that we group by Soma ID, take as many "New gene name"s as there are, remove nulls, sort by alphabetical order, and join with a “;” character:
new_map = df1.groupby("Soma ID")["New gene name"].unique().apply(
lambda arr: ";".join(sorted([a for a in arr if not pd.isnull(a)]))).to_frame().reset_index()
Now we have a table mapping SomaLogic sequence ID to a string of new gene name values. The head of the table looks like this:
And some of those with multiple mappings look like this:
Performing the mapping
Next I want to join these new gene name mappings to the original PPMI sequence IDs.
The PPMI TESTNAME is SomaScan sequence ID with an underscore and an extra number on the end. For example, the entry 12690-33_3 is sequence ID 12690-33, and 3708-62_1 is sequence ID 3708-62. We can create a new column for sequence ID as follows:
df3 = pd.read_excel("ppmi_project_151.xlsx", sheet_name="Lookup")
df3["Soma ID"] = df3["TESTNAME"].apply(lambda s: "_".join(s.split("_")[:-1]))
For ease of understanding, I added a column which indicates whether the Soma ID was in the new 11k mapping file or not (some older sequence IDs have been retired, so they no longer appear in the 11k map):
df3["In 11k?"] = df3["Soma ID"].isin(df1["Soma ID"].unique())
And lastly, perform the join to the new map, and write it out to a new file:
df3 = df3.merge(new_map, how="left", on="Soma ID")
df3.to_csv("ppmi_project_151_lookup_remap_oct2026.csv", index=False)
The file I generated by following this process is attached here. NOTE: there is no PPMI participant data here; this is purely the “Lookup” sheet with extra columns for “Soma ID”, “In 11k?”, and “New gene name”.
ppmi_project_151_lookup_remap_oct2026.csv (571.5 KB)
Analytics
The original PPMI lookup table mapped all 4785 sequence IDs to gene names, but this new mapping includes 377 null gene names. These seem to be due to retired SomaScan IDs, as “In 11k?” is False, which suggests SomaLogic are no longer confident in the precision or accuracy of those aptamers. As a result, I have left them blank for my own analysis (ie removed them), instead of using the old gene names.
When comparing the old mapping to the new mapping, 4221 Soma IDs map to the same gene name(s) as before (out of the 4785 total, or 4408 with a non-null new name). That means only 187 are still in use, but map to different genes than before. And the majority of these appear to be refinements to an existing name, instead of a wholly new gene, such as these:
However, removing those new null mappings seems to make quite a difference to my own downstream analysis. I end up with a much cleaner, smaller, more focused gene set than I was getting before, so updating the mapping was well worth it for me. It confirmed for me that I was getting the same signal as before, but it was a more refined signal with clearer focus.
Next steps
I’d love to hear any feedback on this process, or what results other have seen from updating the PPMI SomaScan mapping!
And huge thanks again to Tiago for kicking off this whole direction of enquiry!



