write.csv() Exporta um Data Frame
Tirar um data frame do R e colocá-lo em um arquivo que outros programas conseguem abrir é uma chamada:
write.csv(df, "results.csv", row.names = FALSE)
Essa é a linha para memorizar - incluindo o row.names = FALSE, que justificaremos em um instante. O primeiro argumento é o data frame, o segundo é o nome do arquivo, e a função não retorna nada: seu trabalho inteiro é o efeito colateral de criar o arquivo.
Você pode ver exatamente o que o write.csv() produz sem tocar no disco, porque ele aceita qualquer conexão onde espera um nome de arquivo - incluindo stdout(), que apenas imprime:
Eis o seu CSV: uma linha de cabeçalho, uma linha por registro, valores de texto entre aspas, números sem elas. (As aspas são o padrão quote = TRUE; todo leitor de CSV convencional lida com elas, então deixe como está.) Gravar em um arquivo de verdade é a mesma chamada com "results.csv" no lugar de stdout().
Por Que row.names = FALSE Deve Ser Seu Padrão
Todo data frame carrega nomes de linha - geralmente só os números 1, 2, 3... - e o padrão do write.csv() é gravá-los no arquivo como uma primeira coluna extra com cabeçalho vazio. Veja como fica a saída padrão, e o que acontece quando esse arquivo faz a viagem de volta para o R:
A exportação ganhou uma primeira coluna sem nome contendo "1", "2" - e na reimportação o read.csv() teve de chamá-la de alguma coisa, então inventou X. Essa é a coluna misteriosa X que intriga todo iniciante: não é dado, é a numeração de linhas do R vazando para o seu arquivo. Repita isso algumas vezes num ciclo de exporta-importa e você acumula X, X.1, X.2...
Nomes de linha quase nunca carregam informação real - e quando carregam, a informação pertence a uma coluna de verdade de qualquer forma. Então torne row.names = FALSE um reflexo. Ele deveria, discutivelmente, ser o padrão; não é, por razões de retrocompatibilidade, então o hábito fica por sua conta.
Onde o Arquivo Vai Parar
write.csv(df, "results.csv") grava no diretório de trabalho do R - e se ele não for onde você esperava, o arquivo parece desaparecer. Ele não falhou; está apenas na pasta que getwd() imprime. É a mesma armadilha da leitura, em imagem espelhada, e as mesmas duas ferramentas a resolvem:
getwd() # the folder your file went to
write.csv(df, "C:/Users/ada/results/out.csv", row.names = FALSE) # or: remove all doubt
Caminhos completos (barras normais funcionam em toda plataforma, inclusive Windows) tornam os scripts inequívocos. Como o diretório de trabalho é definido e gerenciado está em diretório de trabalho.
write.table() para Outros Delimitadores - e a Questão do Append
O write.csv() é, na verdade, um invólucro fino sobre o write.table() de propósito geral, pré-configurado para vírgulas. Chame write.table() diretamente quando precisar de outro formato - mais comumente separado por tabulação:
Separado por tabulação, sem aspas - o formato que muitas ferramentas seguintes preferem. Repare que com write.table() você define tudo explicitamente; ele não herda a vírgula nem as aspas padrão do write.csv.
Uma coisa que o write.csv() se recusa a fazer é append: passar append = TRUE rende um aviso de que a opção foi ignorada, porque anexar reescreveria a linha de cabeçalho no meio do arquivo. Se você genuinamente precisa adicionar linhas a um CSV existente - digamos, registrando resultados entre execuções - desça ao write.table() e suprima o cabeçalho você mesmo:
write.table(new_rows, "log.csv", sep = ",",
append = TRUE, col.names = FALSE, row.names = FALSE)
E se você está no tidyverse, readr::write_csv(df, "out.csv") é a contraparte do read_csv(): mesma saída, mais rápido em data frames grandes, e ele nunca grava nomes de linha - um argumento a menos para lembrar.
saveRDS() e readRDS(): Round-Trips Exatos
CSV é um formato com perdas. Ele armazena texto, e ponto final - então uma coluna Date vira a string "2026-08-07", um factor vira seus rótulos com a ordenação de níveis perdida, atributos somem, e cada tipo precisa ser adivinhado de novo na importação (geralmente com acerto, às vezes não - zeros à esquerda, estamos olhando para vocês). E um CSV só comporta uma tabela retangular: um modelo ajustado, uma lista, uma matriz com dimnames não têm representação alguma em CSV.
Quando o arquivo é para o R - salvar os dados limpos de hoje para a sessão de amanhã - pule os formatos de texto por completo:
saveRDS(df, "clean_data.rds") # today
df <- readRDS("clean_data.rds") # tomorrow: identical object, types intact
saveRDS() serializa um objeto do R, qualquer objeto, exatamente. readRDS() o devolve byte a byte: factors mantêm seus níveis, datas continuam datas, nada é adivinhado de novo. Funciona com qualquer coisa - um data frame, uma lista de data frames, um modelo de regressão ajustado. O arquivo .rds é binário comprimido, então costuma ser menor que o CSV também. O único custo é que nada além do R consegue abri-lo - que é precisamente o motivo da regra prática: CSV para compartilhar com humanos e outras ferramentas, RDS para guardar para você mesmo.
save() e load() - e Por Que o RDS Costuma Vencer
Você também vai encontrar um par mais antigo. save() grava vários objetos nomeados em um único arquivo .RData, e load() os restaura - no seu workspace, com os nomes originais:
save(df, model, params, file = "session.RData")
load("session.RData") # df, model, params silently appear
Essa conveniência é o problema. Quem decide os nomes das variáveis é o load(), não você - ele planta objetos no seu ambiente sem você ver, sobrescrevendo silenciosamente qualquer coisa que já tivesse aqueles nomes. Seis meses depois, ninguém lembra o que session.RData sequer contém sem carregá-lo para descobrir.
readRDS() tem a interface honesta: você escolhe o nome (df <- readRDS(...)), nada é sobrescrito pelas suas costas, e um arquivo significa um objeto. A menos que você precise especificamente empacotar vários objetos em um único arquivo, prefira RDS. (Se precisar de um pacote, colocar os objetos em uma lista nomeada e passar a lista ao saveRDS() dá o mesmo efeito sem nenhuma das surpresas.)
O Que Você Leva
write.csv(df, "out.csv", row.names = FALSE)- a exportação inteira, e sim, sempre com orow.names = FALSE.- A coluna misteriosa
Xna reimportação são os nomes de linha vazando para o arquivo; o padrão os gravou, você não deveria. - Os arquivos vão para o diretório de trabalho -
getwd()encontra exportações "perdidas", caminhos completos as previnem. write.table()lida com outros delimitadores e (comcol.names = FALSE) com append de verdade;write.csv()deliberadamente não anexa.saveRDS()/readRDS()fazem o round-trip exato de qualquer objeto do R - tipos, factors, atributos intactos. CSV para compartilhar, RDS para você.- Prefira RDS a
save()/load(): nomes explícitos, sem sobrescritas silenciosas.
A seguir: um tipo de dado que merece a própria página antes de te morder numa importação - datas.
Perguntas frequentes
Como exporto um data frame para CSV no R?
write.csv(df, "output.csv", row.names = FALSE). A parte do row.names = FALSE importa: sem ela, o R grava seus números de linha internos como uma primeira coluna extra, que aparece como uma coluna misteriosa chamada X quando alguém reimporta o arquivo.
Por que meu CSV exportado tem uma coluna X quando eu o leio de volta?
Porque o padrão do write.csv é row.names = TRUE - ele gravou os números das linhas como uma primeira coluna sem nome, e o read.csv batizou essa coluna de X na volta. Exporte com row.names = FALSE e a coluna fantasma desaparece.
Qual é a diferença entre write.csv e saveRDS no R?
write.csv produz uma tabela em texto puro que qualquer um pode abrir, mas armazena apenas texto - níveis de factor, tipos Date, atributos e objetos não tabulares são achatados ou perdidos, e os tipos precisam ser adivinhados de novo na importação. saveRDS armazena um objeto do R byte a byte; readRDS o devolve exatamente como era. Use CSV para compartilhar com outros, RDS para guardar para suas próprias sessões de R.
Onde o write.csv salva o arquivo?
No diretório de trabalho atual do R, a menos que você informe um caminho completo. Rode getwd() para ver onde é - se seu arquivo parece ter sumido, ele está na pasta que o getwd() imprime. Passar um caminho completo como "C:/Users/ada/results/out.csv" elimina a ambiguidade.