【问题标题】:convert a columnar table of contract info into rows将合同信息的柱状表转换为行
【发布时间】:2020-10-22 08:46:59
【问题描述】:

我有一张包含合同信息的大表格(数百份合同),所有这些信息都收集在表格的单个列中。每个合约占用 6 个连续行。我已经能够添加另一列 (CAT),指示列中每一行的内容:公司、地址、CitySTZip、联系人、合同、职务。 [使用 R]

添加第二列表示列名后数据的可重现版本,数据如下所示:

textFile <- "col1|col2
XYZCo|Company
123 Main Street|Address
Yourtown, MA 12345|CityStZip
Joe Smith|Contact
20-234-56/3|Contract
Process for Work|Title
ZZTop Co|Company
123 Jefferson Street|Address
Chicago, IL 60636|CityStZip
Jane Doe|Contact
23-274-11/3|Contract
Yet Another One|Title"

data <- read.csv(text=textFile,header = TRUE,sep="|")
data


                   col1      col2
1                 XYZCo   Company
2       123 Main Street   Address
3    Yourtown, MA 12345 CityStZip
4             Joe Smith   Contact
5           20-234-56/3  Contract
6      Process for Work     Title
7              ZZTop Co   Company
8  123 Jefferson Street   Address
9     Chicago, IL 60636 CityStZip
10             Jane Doe   Contact
11          23-274-11/3  Contract
12      Yet Another One     Title

我想对所有数据进行重新排序,以便每份合约在数据表中占据一行,并将 CAT 值作为列标题。

根据下面发布的答案,尝试使用 for() 循环重新格式化文件失败。

for(i in 1:nrow(data)){
   for(j in 1:6){
      # got stuck here... 
   }
} 

所需的输出如下所示:

Company Address           CitySTZip            Contact     Contract      Title

XYZCo 123 Main Street   Yourtown, MA 12345   Joe Smith   20-234-56/3   Process for Work

【问题讨论】:

  • 似乎无法让数据显示在帖子的列中:(
  • 欢迎来到 StackOverflow,@xtufer。我的答案中发布的两种潜在数据格式中的任何一种是否正确?如果不是,请使用dput() 发布一小部分数据,如how to write a minimal, reproducible example 中所述。
  • 鉴于您在下面的 cmets,我更新了您的问题以使其可重现。请编辑带有for() 循环的部分,并发布您被卡住的实际代码,以及您的代码收到的任何错误消息。

标签: r reformat


【解决方案1】:

在发布我的原始答案后,我意识到数据可能与我的假设不同,因为原始帖子中的内容引用了原始数据中的第 1 列和第 2 列。如果数据如下所示,则有一个相对简单的答案,将dplyrtidyr::pivot_wider() 结合起来。

首先,我们将读取数据并打印结果数据框,两列包括数据值和列名。

textFile <- "col1|col2
XYZCo|Company
123 Main Street|Address
Yourtown, MA 12345|CityStZip
Joe Smith|Contact
20-234-56/3|Contract
Process for Work|Title
ZZTop Co|Company
123 Jefferson Street|Address
Chicago, IL 60636|CityStZip
Jane Doe|Contact
23-274-11/3|Contract
Yet Another One|Title"
data <- read.csv(text = textFile,header = TRUE, sep="|")

数据框如下所示:

> data
                   col1      col2
1                 XYZCo   Company
2       123 Main Street   Address
3    Yourtown, MA 12345 CityStZip
4             Joe Smith   Contact
5           20-234-56/3  Contract
6      Process for Work     Title
7              ZZTop Co   Company
8  123 Jefferson Street   Address
9     Chicago, IL 60636 CityStZip
10             Jane Doe   Contact
11          23-274-11/3  Contract
12      Yet Another One     Title

为了将数据框转换为宽格式整洁的数据,我们需要添加一个 ID 列来区分一个观察结果和其他观察结果。我们可以为此使用dplyr::mutate(),以及ceiling() 函数。需要ceiling() 函数,因为我们希望每 6 行输入数据的 ID 值保持不变。当我们将seq_along() 的结果除以 6 时,它会生成所需的向量。

添加 ID 列后,转为宽格式相对简单。

library(dplyr)
library(tidyr)
data %>% mutate(id = ceiling(seq_along(col1)/6)) %>%
    pivot_wider(.,id,names_from=col2,values_from=col1)

...和输出:

# A tibble: 2 x 7
     id Company  Address          CityStZip       Contact  Contract   Title       
  <dbl> <chr>    <chr>            <chr>           <chr>    <chr>      <chr>       
1     1 XYZCo    123 Main Street  Yourtown, MA 1… Joe Smi… 20-234-56… Process for…
2     2 ZZTop Co 123 Jefferson S… Chicago, IL 60… Jane Doe 23-274-11… Yet Another…

原答案

这个问题的有趣挑战是观察跨越 6 行数据,但不是在固定的记录布局中,所以我们不能使用 read.fwf()read.fortran() 来读取文件。

相反,我们将使用readLines() 将数据读入向量,然后将其写入临时文件,将每 6 行组合成一个输出记录。最后,我们将使用read.csv() 读取重构后的数据。

原始帖子不清楚列名是否可以与原始数据文件中的其余数据区分开来,因此此解决方案假定我们需要将它们从所需的结果数据框中解析出来。

textFile <- "XYZCo Company
123 Main Street Address
Yourtown, MA 12345 CityStZip
Joe Smith Contact
20-234-56/3 Contract
Process for Work Title
ZZTop Co Company
123 Jefferson Street Address
Chicago, IL 60636 CityStZip
Jane Doe Contact
23-274-11/3 Contract
Yet Another One Title"
    

首先我们用readLines()将数据读入一个字符向量。

dataVector <- readLines(textConnection(textFile))

接下来,我们从向量中剔除列名数据。由于只有6个列名,所以我很懒,只是反复使用sub()。原题表示列名是在数据加载到R后添加的,所以这段代码可能不需要。

# clean column names from raw data 
dataVector <- sub("Company","",dataVector)
dataVector <- sub("Address","",dataVector)
dataVector <- sub("CityStZip","",dataVector)
dataVector <- sub("Contact","",dataVector)
dataVector <- sub("Contract","",dataVector)
dataVector <- sub("Title","",dataVector)

接下来,我们循环遍历向量,并将每 6 行组合成一个输出记录,使用管道 | 作为分隔符,因为数据在 CityStZip 字段中包含逗号。

# write to tempfile as pipe separated values
tmpFile <- "./data/tmpfile.csv"
counter <- 0
outLine <- NULL
for(i in 1:length(dataVector)){
    counter <- counter + 1
    if(counter == 1 ) outLine <- dataVector[i]
    else outLine <- paste(outLine,dataVector[i],sep="|")
    if(counter == 6) {
         cat(outLine,file = "./data/tmpfile.csv",sep="\n",append=TRUE)
         counter <- 0
         outLine <- NULL
    }
}

最后,我们读取刚刚创建的文件,并指定sep = '|' 作为列之间的分隔符。我们还使用col.names 参数来设置列名。

colNames <- c("Company","Address","CityStZip","Contact","Contract","Title")
data <- read.csv("./data/tmpfile.csv",header = FALSE,sep = "|",
                 col.names = colNames)

...和输出:

> data
    Company               Address           CityStZip    Contact     Contract
1    XYZCo       123 Main Street  Yourtown, MA 12345  Joe Smith  20-234-56/3 
2 ZZTop Co  123 Jefferson Street   Chicago, IL 60636   Jane Doe  23-274-11/3 
              Title
1 Process for Work 
2  Yet Another One 

【讨论】:

  • 变异!添加索引是诀窍。我尝试做长手“for循环”,但横向和纵向索引变得复杂。总是对 R 和它的杰出从业者如何提出优雅的答案感到惊讶。谢谢!
  • @xtufer - 如果您认为答案有帮助,请点击问题旁边的复选标记接受并投票。
猜你喜欢
  • 1970-01-01
  • 2018-06-09
  • 2014-01-08
  • 2017-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-27
  • 2021-10-11
相关资源
最近更新 更多