-2

I have a dataframe that I have successfully transposed using the t function present in base R. However I'd like to change the column names and row names to be part of the dataframe. I'll use the iris data as an example.

# Transpose iris data
iris.transposed <- t(iris)

# View the transposed DF
View(iris.transposed)

In the screenshot attached, transposed.iris.df the column header in the transposed DF contains columns V1, V2, V2, V4, e.t.c. However, I'd like the row species to the the column header.

The same goes for the rows. Sepal.length, sepal.width, petal.length.. e.t.c are rownames. However, I'd like them to be part of the dataframe as observations.

How do I finalise this?

3
  • 4
    It is not really advised to have column with the same names. What do you expect iris.transposed$setosa to return? Note also that using t converts your data.frame to a matrix. Commented Feb 11 at 8:14
  • 5
    There's very rarely a good reason to have duplicated column names which is why most data frame creation functions invoke make.unique() on the names by default. Why do you want this? Commented Feb 11 at 8:15
  • That said, you can use colnames(iris.transposed) <- iris.transposed["Species", ] and iris.transposed$col <- rownames(iris.transposed) Commented Feb 11 at 8:16

2 Answers 2

1

Something like this?
Note that the first argument of ave tells its return value class. The new values are not factor levels (Species only has 3 levels) and when the serial numbers are pasted NA's are generated. That's why the first iris[[5]] must be coerced to character.
The result is a "matrix", not a data.frame.

# remove the last column, it's not a numeric column, it's a factor
iris.transposed <- t(iris[-5])
# the first argument of ave() tells the return value class
new_names <- ave(as.character(iris[[5]]), iris[[5]], FUN = \(x) paste0(x, seq_along(x)))
colnames(iris.transposed) <- new_names
# see the matrix structure
str(iris.transposed)
#>  num [1:4, 1:150] 5.1 3.5 1.4 0.2 4.9 3 1.4 0.2 4.7 3.2 ...
#>  - attr(*, "dimnames")=List of 2
#>   ..$ : chr [1:4] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width"
#>   ..$ : chr [1:150] "setosa1" "setosa2" "setosa3" "setosa4" ...

Created on 2025-02-11 with reprex v2.1.1


tidyverse solution

A tidyverse solution is probably better, the output automatically includes the column names of length and width.

library(tidyverse)

iris %>% 
  mutate(Species = paste(Species, row_number(), sep = "_"), .by = Species) %>%
  pivot_longer(-Species, names_to = "Measure") %>%
  pivot_wider(
    id_cols = Measure,
    names_from = Species,
    values_from = value
  )
Sign up to request clarification or add additional context in comments.

Comments

1

Here's a general function to do what you want:

t2 <- function(data, col) {
  t.data <- data.frame(names(data[-col]), t(data[-col]))
  colnames(t.data) <- c(names(data[col]), as.character(data[,col]))
  rownames(t.data) <- NULL
  t.data
}

Call it on the iris dataset:

    t2(iris, col=5)
       Species setosa setosa setosa setosa setosa setosa setosa
1 Sepal.Length    5.1    4.9    4.7    4.6    5.0    5.4    4.6
2  Sepal.Width    3.5    3.0    3.2    3.1    3.6    3.9    3.4
3 Petal.Length    1.4    1.4    1.3    1.5    1.4    1.7    1.4
4  Petal.Width    0.2    0.2    0.2    0.2    0.2    0.4    0.3

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.