write.table writes unwanted leading empty column to header when has rownames
Asked Answered
C

5

103

check this example:

> a = matrix(1:9, nrow = 3, ncol = 3, dimnames = list(LETTERS[1:3], LETTERS[1:3]))
> a
  A B C
A 1 4 7
B 2 5 8
C 3 6 9

the table displays correctly. There are two different ways of writing it to file...

write.csv(a, 'a.csv') which gives as expected:

"","A","B","C"
"A",1,4,7
"B",2,5,8
"C",3,6,9

and write.table(a, 'a.txt') which screws up

"A" "B" "C"
"A" 1 4 7
"B" 2 5 8
"C" 3 6 9

indeed, an empty tab is missing.... which is a pain in the butt for downstream things. Is this a bug or a feature? Is there a workaround? (other than write.table(cbind(rownames(a), a), 'a.txt', row.names=FALSE)

Cheers, yannick

Codd answered 19/3, 2010 at 15:9 Comment(0)
H
154

Citing ?write.table, section CSV files:

By default there is no column name for a column of row names. If col.names = NA and row.names = TRUE a blank column name is added, which is the convention used for CSV files to be read by spreadsheets.

So you must do

write.table(a, 'a.txt', col.names=NA)

and you get

"" "A" "B" "C"
"A" 1 4 7
"B" 2 5 8
"C" 3 6 9
Hygienics answered 19/3, 2010 at 15:45 Comment(8)
@Marek, would it be possible to add a name to the rownames column? I.e., instead of "", add "ID" or something alike?Lisle
@Lisle From what I know you can't. You may bind row names with data and give them names (as like in question).Hygienics
@rusalkaguy Your edit has no point. This "extension" is in original question ("workaround other than")Hygienics
How would you get the number in each column to line up under the col names?Pounce
@Pounce You mean fixed width format? Look at write.fwf from gdata package. And ask new question instead of comment. And why you ever need that?!Hygienics
@Hygienics The write.fwf packages doesn't work. The column names are spaced differently from the column values. I need this because I'm trying to write a GAMS file using R and TABLE columns need to overlap their header.Pounce
@Hygienics you can use tibble to convert rownames to column with a desired ID: tibble::rownames_to_column(var = "ID")Drastic
@ÖmerAn Well, tibble was not avaiable at the time when answer was posted ;)Hygienics
M
12

A slight modification to @Marek very helpful answer WILL add a header to the rownames column: temporarily add the rownames as the first column in the data.frame, and write that, ignoring the real rownames.

> a = matrix(1:9, nrow = 3, ncol = 3, dimnames = list(LETTERS[1:3], LETTERS[1:3]))
> write.table(data.frame("H"=rownames(a),a),"a.txt", row.names=FALSE)

and you get

"H" "A" "B" "C"
"A" 1 4 7
"B" 2 5 8
"C" 3 6 9
Monanthous answered 7/7, 2016 at 21:16 Comment(1)
You should edit Marek's answer to include this, I think.Sneaking
B
6

For anyone working in the tidyverse (dplyr, etc.), the rownames_to_column() function from the tibble package can be used to easily convert row.names to a column, e.g.:

library('tibble')
a = as.data.frame(matrix(1:9, nrow=3, ncol=3, 
                  dimnames=list(LETTERS[1:3], LETTERS[1:3])))

a %>% rownames_to_column('my_id')

  my_id A B C
1     A 1 4 7
2     B 2 5 8
3     C 3 6 9

Combining this with the row.names=FALSE option in write.table() results in output with header names for all columns.

Butterball answered 26/1, 2017 at 19:30 Comment(0)
D
0

I revised a simple function from @mnel, which adds flexibility by using connections. Here is the function:

my.write <- function(x, file, header, f = write.csv, ...){
# create and open the file connection
datafile <- file(file, open = 'wt')
# close on exit 
on.exit(close(datafile))
# if a header is defined, write it to the file (@CarlWitthoft's suggestion)
if(!missing(header)) {
writeLines(header,con=datafile, sep='\t')
writeLines('', con=datafile, sep='\n')
}
# write the file using the defined function and required addition arguments  
f(x, datafile,...)
}

You can specify the function to be 'write.table', 'write.csv', 'write.delim' etc.

Cheers!

Dour answered 3/5, 2015 at 18:35 Comment(0)
A
0

I wanted to add an ad-hoc solution that I haven't seen in this question or any linked questions already. Users may want to write a table with the formatting described above, but have a label present at the top of the first column (where the "" is in the accepted answer). The second answer presents a solution which works, but doesn't allow certain characters (and will replace " " characters with "."s, for example). This solution achieves same, but allows any character type permitted by R to be present in the header.

Starting with a matrix 'a' which has the headers you'd like to use, less the header for the row_names column:

headers <- c("row_names", colnames(a)) #create headers, a character vector that will serve as the header line for your output file, "row_names" here can be replaced with a heading of your choice.
test <- data.frame("row_names"=rownames(a),a) #create data frame with "row_names" for top left position, and the existing row names as the first column. 
test <- as.matrix(rbind(headers, test)) #combine prepared headers with data frame above
write(t(test), "test_output.txt", sep = "\t", ncolumns = dim(test)[[2]]) #write output as a tab-separated file. object must be transposed for this command to produce a table in the expected orientation.
Arsis answered 31/10, 2023 at 19:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.