Jak zamienić małymi literami kolumnę łańcucha danych pandy, jeśli ma brakujące wartości?

87

Poniższy kod nie działa.

import pandas as pd
import numpy as np
df=pd.DataFrame(['ONE','Two', np.nan],columns=['x']) 
xLower = df["x"].map(lambda x: x.lower())

Jak powinienem to zmienić, aby uzyskać xLower = ['one', 'two', np.nan]? Wydajność jest ważna, ponieważ rzeczywista ramka danych jest ogromna.

P.Escondido
źródło
Od wersji 0.25 polecam str.casefoldbardziej agresywne porównania ciągów ze składaniem wielkości liter. Więcej informacji w tej odpowiedzi .
cs95

Odpowiedzi:

189

używaj pand wektoryzowanych metod ciągów ; jak w dokumentacji:

metody te automatycznie wykluczają brakujące wartości / NA

.str.lower() to pierwszy przykład;

>>> df['x'].str.lower()
0    one
1    two
2    NaN
Name: x, dtype: object
behzad.nouri
źródło
co ciekawe, jest to wolniejsze niż metoda mapy w drugiej odpowiedzi w 10000 loops, best of 3: 96.4 µs per loopporównaniu z10000 loops, best of 3: 125 µs per loop
EdChum
1
@EdChum, co nie jest zaskakujące, mając tylko 3 elementy; ale nie byłoby tak w przypadku, powiedzmy, zaledwie 100 elementów;
behzad.nouri
@ behzad.nouri Próbowałem df1 ['komentarz'] = df1 ['komentarz']. str.lower (), ale otrzymałem błąd KeyError: 'komentarz' za każdym razem. Sprawdziłem - mam kolumnę o nazwie exaclty tak samo. Co może spowodować błąd?
Katya
16

Innym możliwym rozwiązaniem, w przypadku gdy kolumna zawiera nie tylko łańcuchy, ale także liczby, jest użycie astype(str).str.lower()lub to_string(na_rep='')ponieważ w przeciwnym razie, biorąc pod uwagę, że liczba nie jest łańcuchem, po obniżeniu zwróci NaN, dlatego:

import pandas as pd
import numpy as np
df=pd.DataFrame(['ONE','Two', np.nan,2],columns=['x']) 
xSecureLower = df['x'].to_string(na_rep='').lower()
xLower = df['x'].str.lower()

potem będzie:

>>> xSecureLower
0    one
1    two
2   
3      2
Name: x, dtype: object

i nie

>>> xLower
0    one
1    two
2    NaN
3    NaN
Name: x, dtype: object

edytować:

jeśli nie chcesz zgubić NaNów, to lepiej będzie używać mapy (od @ wojciech-walczak i komentarza @ cs95) będzie wyglądać mniej więcej tak

xSecureLower = df['x'].map(lambda x: x.lower() if isinstance(x,str) else x)
Mike W.
źródło
1
Dzięki! Zapomniałem o NaNs, właśnie poprawiłem odpowiedź
Mike W
7

możesz spróbować również tego,

df= df.applymap(lambda s:s.lower() if type(s) == str else s)
Farid
źródło
1
type(s) == strzamiast tego powinno byćisinstance(s, str)
cs95
7

Możliwe rozwiązanie:

import pandas as pd
import numpy as np

df=pd.DataFrame(['ONE','Two', np.nan],columns=['x']) 
xLower = df["x"].map(lambda x: x if type(x)!=str else x.lower())
print (xLower)

A wynik:

0    one
1    two
2    NaN
Name: x, dtype: object

Nie jestem jednak pewien co do wydajności.

Wojciech Walczak
źródło
Tak samo jak w przypadku drugiej odpowiedzi, użyj isinstancepodczas sprawdzania typu obiektu.
cs95
6

Pandy> = 0,25: Usuń rozróżnienia wielkości liter za pomocą str.casefold

Począwszy od wersji 0.25, polecam użycie metody ciągów „wektoryzowanych”, str.casefoldjeśli masz do czynienia z danymi Unicode (działa niezależnie od ciągu znaków lub Unicode):

s = pd.Series(['lower', 'CAPITALS', np.nan, 'SwApCaSe'])
s.str.casefold()

0       lower
1    capitals
2         NaN
3    swapcase
dtype: object

Zobacz także powiązany problem z usługą GitHub GH25405 .

casefoldnadaje się do bardziej agresywnego porównania składania skrzynek. Z wdziękiem obsługuje również NaN (tak jakstr.lower ).

Ale dlaczego to jest lepsze?

Różnica jest widoczna w przypadku Unicode. Biorąc przykład z dokumentacji Pythonastr.casefold ,

Zwijanie liter jest podobne do małych liter, ale bardziej agresywne, ponieważ ma na celu usunięcie wszystkich rozróżnień wielkości liter w ciągu. Na przykład niemiecka mała litera 'ß'jest równoważna z "ss". Ponieważ jest już małe, lower()nic nie zrobi 'ß'; casefold() konwertuje to na "ss".

Porównaj dane wyjściowe lowerdla,

s = pd.Series(["der Fluß"])
s.str.lower()

0    der fluß
dtype: object

Versus casefold,

s.str.casefold()

0    der fluss
dtype: object

Zobacz także Python: lower () vs. casefold () w dopasowywaniu ciągów i konwertowaniu na małe litery .

cs95
źródło
2

Może używać rozumienia z list

import pandas as pd
import numpy as np
df=pd.DataFrame(['ONE','Two', np.nan],columns=['Name']})
df['Name'] = [str(i).lower() for i in df['Name']] 

print(df)
głębokie
źródło
2

Zastosuj funkcję lambda

df['original_category'] = df['original_category'].apply(lambda x:x.lower())
Aravinda_gn
źródło
1

Użyj funkcji zastosuj,

Xlower = df['x'].apply(lambda x: x.upper()).head(10) 
Ashutosh Shankar
źródło
1
Ponieważ wydajność jest ważna dla użytkownika (Efficiency is important since the real data frame is huge.)i jest jeszcze kilka odpowiedzi, spróbuj ujawnić, która z nich jest dobrym punktem odpowiedzi.
David García Bodego
0

skopiuj kolumnę Dataframe i po prostu zastosuj

df=data['x']
newdf=df.str.lower()
Ch HaXam
źródło