This is a test version of Biostars. For the public version, visit https://www.biostars.org.
how to avoid R automatically converting strings to numbers

Suppose I have a tab delimited file as the following:

chr1 234 3.24
chr1 345 2.11
chr2 123 8.99
...
chrX 879 0.24
...

Then in R, I use "read.table" to read the file into a variable "d", the head of the "d" looks normal chr1 234 3.24 chr1 345 2.11 chr2 123 8.99 ...

But when I use "cbind(d[,1], d[,2], d[,3])" and assign it to another variable, say, "b", then "b" looks like

1 234 3.24
1 345 2.11
2 123 8.99
...
23 879 0.24 # "chrX" is automatically converted to "23"
...

That is odd. It looks like "cbind" treats characters as factors and used the factor numbers (e.g. 1, 2, ..., 23) to replace the strings (chr1, chr2, ..., chrX).

How to avoid this?

I know this might not be the best forum to ask the question, but since you guys are so great and I believe some of you have the answers!

r software error

since you guys are so great

You don't want to do the necessary "re"search on web?

I certainly did but found no answers. Weird.

So the problem is while reading the table, strings are read As Factors? Is that True?

The problem is "cbind" automatically convert strings to factor numbers, e.g. "chr1" => "1", "chrM" => "23", "chrX" => "24", "chrM" to "25".

strings are read As Factors? Is that True?

;-)

3 answers

Although the point in the comments about stringsAsFactors option is TRUE :-), the real problem in your specific case is that cbind is coercing your data into a matrix. In R, a matrix, by definition, can only have a single data type. All integer, numeric, character or factor. See the following code examples:

# stringsAsFactors = TRUE
# wrong because the factors are coerced as numeric.
d <- data.frame(
  chr = c("A", "B"),
  start = c(1, 2),
  stringsAsFactors = TRUE
)
cbind(d$chr, d$start)
     [,1] [,2]
[1,]    1    1
[2,]    2    2

# stringsAsFactors = FALSE
# wrong because the numbers are coerced as character.
d <- data.frame(
  chr = c("A", "B"),
  start = c(1, 2),
  stringsAsFactors = FALSE
)
cbind(d$chr, d$start)
     [,1] [,2]
[1,] "A"  "1" 
[2,] "B"  "2"

So, if you use cbind, no matter how you set stringsAsFactors originally or whether you use readr or any other tool to read your data you screw, because a matrix can only have one type of data and you have two. The solution is to use a data.frame, which can handle different data types:

data.frame(chr2 = d$chr, start2 = d$start)
  chr2 start2
1    A      1
2    B      2

Don't forget to set stringsAsFactors as desired.

EDIT:

Note that cbind is doing this because you are passing two vectors. If you pass them as data.frame, cbind treats them as such and this problem is avoided:

cbind(d[, "chr", drop = FALSE], d[, "start", drop = FALSE])
  chr start
1   A     1
2   B     2

Of course, this solution is a lot more verbose.

Good catch, I stopped reading after seeing the stringsAsFactors issue.

Thanks. Almost gave up myself because there were a lot of good comments. A love working with R but these nuances can be really frustrating.

This is the best answer!

"cbind" causes a lot of problems, and using "data.frame" the way you mentioned resolved all the troublesome issues.

This is a benefit of using the readr package rather than base R when reading tables, the stringsAsFactors option (this is what you were looking for) is set in a more coherent way.

actually just checked, "read.table" takes "stringsAsFactors" and it worked!

Thanks!

Remember to "accept" the answers (use check mark against the answers) that solved your problem. You can choose more than one.

The correct way -- and by correct, I mean correct: to specify with complete precision -- to solve this is to use colClasses with read.table, which coerces columns into type classes, like character, numeric, factor, etc.

For instance, in your case:

read.table(someFile, ..., colClasses=c("character", "numeric", "numeric"))

See ?read.table for more information.

OK, this works.

However, if the column names of "someFile" varies from file to file, it would be impossible to predefine which column is "character" or "numeric". Is there a way to force "cbind"?

Thanks.

You can pass stringsAsFactors = FALSE to read.table so no need to specify all the column classes.

Also argument as.is = TRUE does the same trick.

Log in to answer this question.