Wie können Sie doppelte Werte in Pandas durch mehrere eindeutige Zeichenfolgen ersetzen?

8
import pandas as pd
import numpy as np
data = {'Name':['Tom', 'Tom', 'Jack', 'Terry'], 'Age':[20, 21, 19, 18]} 
df = pd.DataFrame(data)

Nehmen wir an, ich habe einen Datenrahmen, der so aussieht. Ich versuche herauszufinden, wie die Spalte Name auf den Wert 'Tom' überprüft werden kann. Wenn ich ihn beim ersten Ersetzen durch den Wert 'FirstTom' finde und beim zweiten Erscheinen durch den Wert 'SecondTom' ersetze. . Wie erreichen Sie das? Ich habe die Ersetzungsmethode schon einmal verwendet, aber nur, um alle Toms durch einen einzigen Wert zu ersetzen. Ich möchte keine 1 am Ende des Werts hinzufügen, sondern die Zeichenfolge vollständig in etwas anderes ändern.

Bearbeiten:

Wenn der df unten eher so aussehen würde, wie würden wir in der ersten und zweiten Spalte nach Tom suchen und dann die erste Instanz durch FirstTom und die zweite Instanz durch SecondTom ersetzen

data = {'Name':['Tom', 'Jerry', 'Jack', 'Terry'], 'OtherName':[Tom, John, Bob,Steve]}

Logan0015
quelle

Antworten:

9

Durch einfaches Hinzufügen zu den vorhandenen Lösungen können Sie inflectein dynamisches Wörterbuch erstellen

import inflect
p = inflect.engine()

df['Name'] += df.groupby('Name').cumcount().add(1).map(p.ordinal).radd('_')
print(df)

        Name  Age
0    Tom_1st   20
1    Tom_2nd   21
2   Jack_1st   19
3  Terry_1st   18
anky
quelle
7

Wir können es tun cumcount

df.Name=df.Name+df.groupby('Name').cumcount().astype(str)
df
     Name  Age
0    Tom0   20
1    Tom1   21
2   Jack0   19
3  Terry0   18

Aktualisieren

suf = lambda n: "%d%s"%(n,{1:"st",2:"nd",3:"rd"}.get(n if n<20 else n%10,"th"))
g=df.groupby('Name')


df.Name=df.Name.radd(g.cumcount().add(1).map(suf).mask(g.Name.transform('count')==1,''))
df
     Name  Age
0  1stTom   20
1  2ndTom   21
2    Jack   19
3   Terry   18

Update 2 für Spalte

suf = lambda n: "%d%s"%(n,{1:"st",2:"nd",3:"rd"}.get(n if n<20 else n%10,"th"))

g=s.groupby([s.index.get_level_values(0),s])
s=s.radd(g.cumcount().add(1).map(suf).mask(g.transform('count')==1,''))
s=s.unstack()
     Name OtherName
0  1stTom    2ndTom
1   Jerry      John
2    Jack       Bob
3   Terry     Steve
YOBEN_S
quelle
1
OP NeedI don't want to add a 1 on the end of the value
Jezrael
Das ist großartig, danke. Was ist nun, wenn es eine zweite Spalte mit Namen gibt und anstatt die Werte vertikal zu überprüfen, wird horizontal nach demselben Namen gesucht?
Logan0015
1
@ Logan0015L Sie können df.groupby (['Name1', 'Name2']) ausführen. Cumcount ()
YOBEN_S
@jezrael Nach unserem Verständnis ist es besser, die Nummer im Namen zu behalten
YOBEN_S
Könnte dies nach der Zeile anstelle der Spalte gruppiert werden?
Logan0015
7

BEARBEITEN: Für eine pro Zeile duplizierte Anzahl verwenden Sie:

df = pd.DataFrame(data = {'Name':['Tom', 'Jerry', 'Jack', 'Terry'], 
                          'OtherName':['Tom', 'John', 'Bob','Steve'],
                          'Age':[20, 21, 19, 18]})

print (df)
    Name OtherName  Age
0    Tom       Tom   20
1  Jerry      John   21
2   Jack       Bob   19
3  Terry     Steve   18

import inflect
p = inflect.engine()

#map by function for dynamic counter
f = lambda i: p.number_to_words(p.ordinal(i))
#columns filled by names
cols = ['Name','OtherName']
#reshaped to MultiIndex Series
s = df[cols].stack()
#counter per groups
count = s.groupby([s.index.get_level_values(0),s]).cumcount().add(1)
#mask for filter duplicates
mask = s.reset_index().duplicated(['level_0',0], keep=False).values
#filter only duplicates and map, reshape back and add to original data
df[cols] = count[mask].map(f).unstack().add(df[cols], fill_value='')
print (df)
       Name  OtherName  Age
0  firstTom  secondTom   20
1     Jerry       John   21
2      Jack        Bob   19
3     Terry      Steve   18

Verwendung GroupBy.cumcountmit Series.map, jedoch nur für doppelte Werte von Series.duplicated:

data = {'Name':['Tom', 'Tom', 'Jack', 'Terry'], 'Age':[20, 21, 19, 18]} 
df = pd.DataFrame(data)

nth = {
0: "First",
1: "Second",
2: "Third",
3: "Fourth"
}

mask = df.Name.duplicated(keep=False)
df.loc[mask, 'Name'] = df[mask].groupby('Name').cumcount().map(nth) + df.loc[mask, 'Name']
print (df)
        Name  Age
0   FirstTom   20
1  SecondTom   21
2       Jack   19
3      Terry   18

Das dynamische Wörterbuch sollte wie folgt aussehen:

import inflect
p = inflect.engine()

mask = df.Name.duplicated(keep=False)
f = lambda i: p.number_to_words(p.ordinal(i))
df.loc[mask, 'Name'] = df[mask].groupby('Name').cumcount().add(1).map(f) + df.loc[mask, 'Name']
print (df)

        Name  Age
0   firstTom   20
1  secondTom   21
2       Jack   19
3      Terry   18
jezrael
quelle
Dies ist eine sehr raffinierte Verwendung von Karte und Cumcount, schön. Fügen Sie vielleicht einen Schritt hinzu, um die Anzahl der möglichen kumulativen Zählungen anzuzeigen und ein Wörterbuch dynamisch aufzubauen.
Datanovice
5

transform

nth = ['First', 'Second', 'Third', 'Fourth']

def prefix(d):
    n = len(d)
    if n > 1:
        return d.radd([nth[i] for i in range(n)])
    else:
        return d

df.assign(Name=df.groupby('Name').Name.transform(prefix))

          Name  Age
0     FirstTom   20
1    SecondTom   21
2         Jack   19
3        Terry   18
4   FirstSteve   17
5  SecondSteve   16
6   ThirdSteve   15
piRSquared
quelle