In order to make data anonymous, I need to replace original IDs with a different set of new IDs, but still have identical original IDs match across all fields after being replaced. The challenge is doing so elegantly across 4 different presentations of IDs in this Pandas DataFrame.
I have real-world data where numerical IDs are found in 4 possible formats:
- Within a list of strings
'["38", "15", "42"]' - Within a list of numbers
[14, 42, 94] - As integers
42 - As floats
1.0
Here is a generalized small DataFrame with all 4 data types.
df = pd.DataFrame([['["38", "15", "42"]', [14, 42, 94], 42, 1.0],\
['["8", "28"]', [1, 4], 8, 94.0], ['["12"]', [12], 12, 12.0]],\
columns = ['CommentsID','AgentID','CaseID','TicketID'])
df
| CommentsID | AgentID | CaseID | TicketID |
| --------------------- | --------------- | ------- | -------- |
| ['38', '15', '42'] | [14, 42, 94] | 42 | 1.0 |
| ['8', '28'] | [1, 4] | 8 | 94.0 |
| ['12'] | [12] | 12 | 12.0 |
For ease of use in a generalized example, I'm just adding 100 to generate a list of 'new IDs'. However, in the actual problem, the list of corresponding new IDs are randomly generated, so no adding 100 throughout to solve for this one.
orig_ids = list(range(100))
new_ids = [x + 100 for x in orig_ids]
What I would like is to find the most efficient way to replace all of the original IDs in the dataframe with the new IDs across these four data types.
Best solution I have so far is to split into three parts:
- Tackle the float and int versions with a
replace()function (this does not affect the lists, even withregex=True):
df = df.replace(orig_ids, new_ids)
- For the lists of integers, use a far-from-Pythonic doubled for-loop to match indices on the ID lists:
def newIDnumbers(datacol):
newlist = []
for i in range(len(datacol)):
numlist = [orig_ids.index(x) for x in df.AgentID[i]]
newlistrow = []
for idx in range(len(numlist)):
newlistrow.append(new_ids[numlist[idx]])
newlist.append(newlistrow)
return newlist
df.AgentID = newIDnumbers(df.AgentID)
df
- For the lists of strings, build string lists of original ids and new ids, then use a far-from-Pythonic doubled for-loop to match indices on the ID lists:
str_orig_ids = [str(x) for x in orig_ids]
str_new_ids = [str(x) for x in new_ids]
def newIDstrings(datacol):
newlist = []
for i in range(len(datacol)):
numlist = [str_orig_ids.index(x) for x in datacol.str.findall(r'"(\d*)"')[i]]
newlistrow = []
for idx in range(len(numlist)):
newlistrow.append(str_new_ids[numlist[idx]])
newlist.append(newlistrow)
return newlist
df.CommentsID = [str(x) for x in newIDstrings(df.CommentsID)]
df
Does anyone have a more elegant and less computationally-heavy way to achieve this output?
df
| CommentsID | AgentID | CaseID | TicketID |
| --------------------- | --------------- | ------- | -------- |
| ['138', '115', '142'] | [114, 142, 194] | 142 | 100.0 |
| ['108', '128'] | [101, 104] | 108 | 194.0 |
| ['112'] | [112] | 112 | 112.0 |