Sostituzione di valori vuoti (spazio bianco) con NaN nei panda


152

Voglio trovare tutti i valori in un frame di dati Pandas che contengono spazi bianchi (qualsiasi quantità arbitraria) e sostituire quei valori con NaNs.

Qualche idea su come migliorare?

Fondamentalmente voglio girare questo:

                   A    B    C
2000-01-01 -0.532681  foo    0
2000-01-02  1.490752  bar    1
2000-01-03 -1.387326  foo    2
2000-01-04  0.814772  baz     
2000-01-05 -0.222552         4
2000-01-06 -1.176781  qux     

In questo:

                   A     B     C
2000-01-01 -0.532681   foo     0
2000-01-02  1.490752   bar     1
2000-01-03 -1.387326   foo     2
2000-01-04  0.814772   baz   NaN
2000-01-05 -0.222552   NaN     4
2000-01-06 -1.176781   qux   NaN

Sono riuscito a farlo con il codice qui sotto, ma l'uomo è brutto. Non è Pythonic e sono sicuro che non è nemmeno l'uso più efficiente dei panda. Ciclo attraverso ogni colonna e faccio la sostituzione booleana contro una maschera di colonna generata applicando una funzione che esegue una ricerca regex di ciascun valore, corrispondente su spazi bianchi.

for i in df.columns:
    df[i][df[i].apply(lambda i: True if re.search('^\s*$', str(i)) else False)]=None

Potrebbe essere ottimizzato un po 'ripetendo solo i campi che potrebbero contenere stringhe vuote:

if df[i].dtype == np.dtype('object')

Ma questo non è molto di un miglioramento

E infine, questo codice imposta le stringhe di destinazione su None, che funziona come le funzioni di Pandas fillna(), ma sarebbe bello per completezza se potessi effettivamente inserire un NaNdirettamente anziché None.


2
Quello che vuoi davvero è essere in grado di usare replacecon una regex ... (forse questo dovrebbe essere richiesto come funzionalità).
Andy Hayden,

3
Ho riscontrato un problema con github per questa funzione: github.com/pydata/pandas/issues/2285 . Sarei grato per le pubbliche relazioni! :)
Chang She,

Per coloro che vogliono trasformare esattamente un singolo carattere vuoto in dispari, vedere questa semplice soluzione di seguito
Ted Petrou,

Risposte:


200

Penso che df.replace()faccia il lavoro, dal panda 0.13 :

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

# replace field that's entirely space (or empty) with NaN
print(df.replace(r'^\s*$', np.nan, regex=True))

produce:

                   A    B   C
2000-01-01 -0.532681  foo   0
2000-01-02  1.490752  bar   1
2000-01-03 -1.387326  foo   2
2000-01-04  0.814772  baz NaN
2000-01-05 -0.222552  NaN   4
2000-01-06 -1.176781  qux NaN

Come sottolineato da Temak , utilizzare df.replace(r'^\s+$', np.nan, regex=True)nel caso in cui i dati validi contengano spazi bianchi.


1
regex è una bandiera booleana. Forse vuoi dire pd.Series(["1", "#", "9", " .", None]).replace(r"( +\.)|#", "X", regex=True).valuesquale dà['1', 'X', '9', 'X', None]
patricksurry

2
2 anni dopo, ho cambiato la risposta accettata a questo, ora che Panda la supporta. Grazie!
Chris Clark,

35
NOTA : se non si desidera che un elemento che contiene spazio in mezzo per essere sostituito con l'utilizzo NaNdf.replace(r'^\s+$', np.nan, regex=True)
Temak

7
Ho provato a usare questo, ma ho scoperto che r '^ \ s * $' dovrebbe essere l'espressione da usare. senza ^ e $ corrisponderà a qualsiasi stringa con due spazi vuoti consecutivi. Anche cambiato + in * per includere la stringa vuota "" nell'elenco di cose da convertire in NaN
Master Yogurt

1
Sto provando la tua soluzione nel mio codice, ma non ha alcun effetto. Sto provando "energy [" Energy Supply "]. Replace (to_replace =" ... ", value = np.NaN)". Volendo cambiare la stringa "..." in valori NaN, ma non fa nulla e restituisce lo stesso frame di dati.
Archan Joshi,

50

Se si desidera sostituire una stringa vuota e i record con solo spazi, la risposta corretta è :

df = df.replace(r'^\s*$', np.nan, regex=True)

La risposta accettata

df.replace(r'\s+', np.nan, regex=True)

Non sostituisce una stringa vuota !, puoi provare te stesso con l'esempio dato leggermente aggiornato:

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'fo o', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', ''],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

Nota anche che "fo o" non viene sostituito con Nan, sebbene contenga uno spazio. Inoltre, è semplice:

df.replace(r'', np.NaN)

Non funziona neanche - provalo.


33

Che ne dite di:

d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

La applymapfunzione applica una funzione a ogni cella del frame di dati.


Che bel miglioramento! Avrei dovuto pensarci a posteriori, ma per qualche motivo mi sono impiccato a fare sostituzioni booleane. Una domanda: c'è un vantaggio nel fare il controllo basestring rispetto a solo str (x) .isspace ()?
Chris Clark,

1
@ChrisClark: Uno dei due va bene, anche se immagino che isinstancesarà un po 'più veloce.
BrenBarn,

13
Il riferimento a "basestring" nel codice sopra non funzionerà in Python 3 .... in tal caso, prova invece a utilizzare "str".
Spike Williams,

4
Si noti che questa soluzione non sostituisce le stringhe vuote ''. Per considerare anche stringhe vuote, utilizzare:d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and (not x or x.isspace()) else x)
tuomastik

18

Lo farò:

df = df.apply(lambda x: x.str.strip()).replace('', np.nan)

o

df = df.apply(lambda x: x.str.strip() if isinstance(x, str) else x).replace('', np.nan)

È possibile rimuovere tutte le str, quindi sostituire str vuote con np.nan.


lambda x: x.str.strip () dovrebbe essere lambda x: x.strip ()? piccolo suggerimento: aggiungi .astype (str) davanti, questo risolve altri problemi di dati per me. Questo funziona per me: df = df.apply ['colonna']. Astype (str) .apply (lambda x: x.strip ()). Replace ('', np.nan)
Wouter

La seconda riga di codice gestisce sia le colonne int / float che quelle di tipo stringa. Bello. Tks!
Kate Stohr,

6

La più semplice di tutte le soluzioni:

df = df.replace(r'^\s+$', np.nan, regex=True)

5

Se stai esportando i dati dal file CSV, può essere semplice come questo:

df = pd.read_csv(file_csv, na_values=' ')

Ciò creerà il frame di dati e sostituirà i valori vuoti come Na


2
Un'altra opzione ... l'uso skipinitialspace=Truerimuove anche qualsiasi spazio bianco dopo il delimitatore che causerebbe qualsiasi lunghezza di spazio bianco, come stringhe vuote da leggere nan. Tuttavia, se si desidera conservare gli spazi iniziali per qualsiasi motivo, questa opzione non è una buona scelta.
Rajshekar Reddy,

1
@RajshekarReddy, per favore, puoi metterlo come risposta da qualche parte, è stato fantastico!
User2321,

2

Per una soluzione molto veloce e semplice in cui si verifica l'uguaglianza rispetto a un singolo valore, è possibile utilizzare il maskmetodo

df.mask(df == ' ')

1

Questi sono tutti vicini alla risposta giusta, ma non direi di risolvere il problema pur rimanendo più leggibile agli altri che leggono il tuo codice. Direi che la risposta è una combinazione della risposta di BrenBarn e del commento di tuomasttik sotto quella risposta . La risposta di BrenBarn utilizza isspacebuiltin, ma non supporta la rimozione di stringhe vuote, come richiesto da OP, e tenderei ad attribuirlo come caso d'uso standard di sostituzione delle stringhe con null.

L'ho riscritto con .apply, quindi puoi chiamarlo su un pd.Serieso pd.DataFrame.


Python 3:

Per sostituire stringhe vuote o stringhe di interi spazi:

df = df.apply(lambda x: np.nan if isinstance(x, str) and (x.isspace() or not x) else x)

Per sostituire le stringhe di interi spazi:

df = df.apply(lambda x: np.nan if isinstance(x, str) and x.isspace() else x)

Per usarlo in Python 2, dovrai sostituirlo strcon basestring.

Python 2:

Per sostituire stringhe vuote o stringhe di interi spazi:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and (x.isspace() or not x) else x)

Per sostituire le stringhe di interi spazi:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

1

Questo ha funzionato per me. Quando ho importato il mio file CSV ho aggiunto na_values ​​= ''. Gli spazi non sono inclusi nei valori NaN predefiniti.

df = pd.read_csv (filepath, na_values ​​= '')


0

puoi anche usare un filtro per farlo.

df = PD.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '])
    df[df=='']='nan'
    df=df.astype(float)

Ogni riga di questo codice (esclusi i dati) è difettosa.
Giulio

0
print(df.isnull().sum()) # check numbers of null value in each column

modifiedDf=df.fillna("NaN") # Replace empty/null values with "NaN"

# modifiedDf = fd.dropna() # Remove rows with empty values

print(modifiedDf.isnull().sum()) # check numbers of null value in each column

0

Questa non è una soluzione elegante, ma ciò che sembra funzionare è salvare su XLSX e quindi reimportarlo. Le altre soluzioni in questa pagina non hanno funzionato per me, non so perché.

data.to_excel(filepath, index=False)
data = pd.read_excel(filepath)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.