Substituindo valores em branco (espaço em branco) por NaN em pandas

150

Quero encontrar todos os valores em um quadro de dados do Pandas que contenham espaço em branco (qualquer quantidade arbitrária) e substituir esses valores por NaNs.

Alguma idéia de como isso pode ser melhorado?

Basicamente, quero transformar isso:

                   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     

Nisso:

                   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

Consegui fazer isso com o código abaixo, mas cara, é feio. Não é Pythonic e tenho certeza que também não é o uso mais eficiente de pandas. Eu percorro cada coluna e faço a substituição booleana de uma máscara de coluna gerada aplicando uma função que faz uma pesquisa regex de cada valor, correspondendo no espaço em branco.

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

Ele pode ser otimizado um pouco, apenas repetindo os campos que podem conter cadeias vazias:

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

Mas isso não melhora muito

E, finalmente, esse código define as seqüências de destino como None, que funciona com as funções do Pandas fillna(), mas seria ótimo se eu pudesse inserir um NaNdiretamente em vez de None.

Chris Clark
fonte
2
O que você realmente deseja é poder usar replacecom uma regex ... (talvez isso deva ser solicitado como um recurso).
Andy Hayden
3
Eu fiz um problema no github para esse recurso: github.com/pydata/pandas/issues/2285 . Ficaria grato por PRs! :)
Chang She
Para aqueles que querem transformar exatamente um único caractere em branco para faltando, consulte esta solução simples abaixo
Ted Petrou

Respostas:

198

Eu acho que df.replace()faz o trabalho, já que os pandas 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))

Produz:

                   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

Como Temak apontou, use df.replace(r'^\s+$', np.nan, regex=True)caso seus dados válidos contenham espaços em branco.

patricksurry
fonte
1
regex é um sinalizador booleano. Talvez você quer dizer pd.Series(["1", "#", "9", " .", None]).replace(r"( +\.)|#", "X", regex=True).valuesque dá['1', 'X', '9', 'X', None]
patricksurry
2
Dois anos depois, mudei a resposta aceita, agora que os pandas a apóiam. Obrigado!
Chris Clark
35
NOTA : se você não quer um elemento contendo espaço no meio para ser substituído com o uso NaNdf.replace(r'^\s+$', np.nan, regex=True)
Temak
7
Tentei usar isso, mas descobri que r '^ \ s * $' deve ser a expressão a ser usada. sem ^ e $, ele corresponderá a qualquer sequência com dois espaços em branco consecutivos. Também mudou + para * para incluir a string vazia "" na lista de coisas a se converter ao NaN
Mestre Yogurt
1
Estou tentando sua solução no meu código, mas não tem efeito. Estou tentando "energia [" fornecimento de energia "]. Replace (to_replace =" ... ", value = np.NaN)". Deseja alterar a cadeia "..." para os valores NaN, mas não faz nada e retorna o mesmo quadro de dados.
precisa saber é o seguinte
49

Se você deseja substituir uma sequência e registros vazios por apenas espaços, a resposta correta é !

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

A resposta aceita

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

Não substitui uma string vazia !, você pode tentar o mesmo exemplo ligeiramente atualizado:

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'))

Observe também que 'fo o' não é substituído por Nan, embora contenha um espaço. Nota adicional, que um simples:

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

Também não funciona - experimente.

Philipp Schwarz
fonte
33

E se:

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

A applymapfunção aplica uma função a todas as células do quadro de dados.

BrenBarn
fonte
Que melhoria agradável! Eu deveria ter pensado nisso em retrospecto, mas fiquei com vontade de fazer substituições booleanas por algum motivo. Uma pergunta - existe uma vantagem em fazer a verificação de base versus apenas str (x) .isspace ()?
Chris Clark
1
@ ChrisClark: Qualquer um está bem, embora eu ache que o isinstanceserá um pouco mais rápido.
BrenBarn
13
A referência a "basestring" no código acima não funcionará no Python 3 .... nesse caso, tente usar "str".
Spike Williams
4
Observe que esta solução não substitui cadeias vazias ''. Para considerar também cadeias vazias, use:d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and (not x or x.isspace()) else x)
tuomastik
18

Eu vou fazer isso:

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

ou

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

Você pode remover todos os str e substituir str vazio por np.nan.

Xiaorong Liao
fonte
lambda x: x.str.strip () deve ser lambda x: x.strip ()? sugestão secundária: adicione .astype (str) na frente, isso resolve outros problemas de dados para mim. Isso funciona para mim: df = df.apply ['column']. Astype (str) .apply (lambda x: x.strip ()). Replace ('', np.nan)
Wouter
A segunda linha de código lida com colunas do tipo int / float e string. Agradável. Tks!
Kate Stohr
6

Mais simples de todas as soluções:

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

Se você estiver exportando os dados do arquivo CSV, pode ser tão simples quanto isto:

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

Isso criará o quadro de dados e substituirá os valores em branco como Na

ibrahim rupawala
fonte
2
Outra opção..utilizar skipinitialspace=Truetambém remove qualquer espaço em branco após o delimitador, o que causaria qualquer comprimento de espaço em branco, como cadeias de caracteres vazias nan. No entanto, se você deseja manter os espaços iniciais por qualquer motivo, essa opção não é uma boa escolha.
Rajshekar Reddy
1
@RajshekarReddy você pode colocar isso como resposta em algum lugar, isso foi brilhante!
usar o seguinte comando
2

Para uma solução muito rápida e simples em que você verifica a igualdade em relação a um único valor, você pode usar o maskmétodo

df.mask(df == ' ')
Ted Petrou
fonte
1

Tudo isso é quase a resposta certa, mas eu não diria que resolveria o problema, permanecendo mais legível para outras pessoas que lêem seu código. Eu diria que a resposta é uma combinação da resposta de BrenBarn e do comentário de tuomasttik abaixo dessa resposta . A resposta de BrenBarn utiliza o isspacebuiltin, mas não suporta a remoção de cadeias vazias, conforme solicitado pelo OP, e eu tenderia a atribuir isso como o caso de uso padrão da substituição de cadeias por nulas.

Eu o reescrevi com .apply, para que você possa chamá-lo em um pd.Seriesou pd.DataFrame.


Python 3:

Para substituir cadeias vazias ou cadeias de espaços inteiramente:

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

Para substituir seqüências de caracteres de espaços inteiramente:

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

Para usar isso no Python 2, você precisará substituir strpor basestring.

Python 2:

Para substituir cadeias vazias ou cadeias de espaços inteiramente:

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

Para substituir seqüências de caracteres de espaços inteiramente:

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

Isso funcionou para mim. Quando importo meu arquivo csv, adicionei na_values ​​= ''. Os espaços não estão incluídos nos valores padrão de NaN.

df = pd.read_csv (caminho do arquivo, na_values ​​= '')

sambrowne
fonte
0

você também pode usar um filtro para fazer isso.

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)
ERIC
fonte
Todas as linhas deste código (sem incluir dados) estão com defeito.
Julius
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
Jayantha
fonte
0

Essa não é uma solução elegante, mas o que parece funcionar está salvando no XLSX e depois importando-o de volta. As outras soluções nesta página não funcionaram para mim, não sei por quê.

data.to_excel(filepath, index=False)
data = pd.read_excel(filepath)
David Kong
fonte