【问题标题】:extract the same column (different length) from multiple .csv and cbind in one data frame - R从多个 .csv 和 cbind 中提取同一列(不同长度)在一个数据帧中 - R
【发布时间】:2018-05-20 07:47:29
【问题描述】:

所以我有 1300 个财务数据的 csv 文件,格式如下:

          Date     Open     High      Low    Close
1 Nov 28, 2017 0.233394 0.234871 0.223832 0.225542 
2 Nov 27, 2017 0.225910 0.234219 0.212298 0.233113
3 Nov 26, 2017 0.229367 0.235126 0.215153 0.226367
4 Nov 25, 2017 0.234212 0.239257 0.223383 0.228617
5 Nov 24, 2017 0.215836 0.236280 0.209834 0.234195
6 Nov 23, 2017 0.228887 0.232974 0.214334 0.216585

我的目标是从每个文件中提取“打开”列并将这些列绑定在一起,使其看起来像这样:

     Date     "File1"   "File2"  "File3"  ... "File 1300"         
1 Nov 28, 2017 0.233394 0.234871 0.223832 ... 0.225542 
2 Nov 27, 2017 0.225910 0.234219 0.212298 ... 0.233117 
3 Nov 26, 2017 0.229367 0.235126 0.215153 ... 0.226367 
4 Nov 25, 2017 0.234212 NA       0.223383 ... 0.228617 
5 Nov 24, 2017 0.215836 NA       0.209834 ... 0.234195 
6 Nov 23, 2017 0.228887 NA       0.214334 ... NA

我知道这里已经有很多关于如何从多个 csv 文件中提取列以及如何将它们绑定在一起的问题,我快到了,但问题是文件的长度不同我有基于日期的观察。例如,一个文件可以有 2014 年 11 月 3 日的观测值,而另一个文件可以有 2017 年 11 月 23 日的观测值。否则,文件的格式相同,它们的最后观测值都是 2017 年 11 月 28 日。

到目前为止,您可以在下面看到我的代码

# Get a List of all files in directory 
filenames <- list.files(".../path, pattern="*.csv", full.names=F)

# Loading column "Open" from each file 
for(i in filenames){   
    filepath <- file.path(".../path", paste(i,sep=""))   
    assign(i, read.csv(filepath, header=T, sep = ";", 
        colClasses=c(NA, NA, "NULL", "NULL", "NULL"))) 
}

#making a list of all data frames
df_list <- lapply(ls(), function(x) if (class(get(x)) == "data.frame") get(x)) 

#merging  
library(dplyr) 
res2 <- Reduce(function(...) left_join(..., by=c("Date")), df_list)

如您所见,我将所有 csv 文件加载到全局环境中并将其添加到一个列表中,然后我想将列合并到一个数据框中。问题似乎是合并部分,Reduce 或 dplyr-package 似乎无法解决问题。

所以我的问题是,您是否有任何解决方案可以将所有日期列绑定在一个数据框中并按日期排序?而且,是否有任何快速解决方法可以将文件名作为列的标题?

我也不确定 R 是否是解决此问题的最佳方法。我对 Python 不是很熟悉,但如果您认为它更简单,我可以尝试使用它。

【问题讨论】:

    标签: python r financial


    【解决方案1】:

    这是未经测试的。关于如何导入的两种方法。一个是使用 for 循环进行迭代,而第二个是导入所有数据,然后使用 Reduce 将 2. 对象合并到第一个,第三个合并 1. 和 2.,第四个合并 1.、2. 和 3 .等等。

    # approach #1
    result <- read.csv(filenames[1], header = TRUE, sep = ";")[, c("Date", "Open")]
    
    for (i in filenames[-1]) {
      out <- read.csv(i, header = TRUE, sep = ";")[, c("Date", "Open")]
      colnames(out) <- c("Date", basename(filenames[i]))
      result <- merge(result, by = "Date")
    }
    
    
    # approach #2
    alldata <- lapply(filenames, FUN = function(x) {
      out <- read.csv(i, header = TRUE, sep = ";")[, c("Date", "Open")]
      colnames(out) <- c("Date", basename(x))
      out
    })
    
    result <- Reduce(function(dtf1, dtf2) merge(dtf1, dtf2, by = "Date", all = TRUE), alldata)
    

    【讨论】:

    • 感谢您提供两种不同的选择。第一种方法似乎没有遍历所有文件,因为它只输出“out”作为来自第二个文件的数据。我认为第二种方法看起来更快,但合并函数似乎无法识别“日期”,因为我收到此错误:“ fix.by(by.x, x) 中的错误:'by' 必须指定唯一有效的列",我在尝试最初的方法时遇到的问题几乎相同。
    • 我想必须在方法号的lapply 中的函数中添加一个return(out)。 2.
    • 谢谢cryo111!这几乎解决了它。我现在唯一的问题是日期的顺序不正确。例如,我得到了 2017 年 8 月的所有日期,然后是所有 dember 等。我可以按日期排序,但如果我能从一开始就按正确的顺序得到它会更整洁。
    • 我在任何地方都看不到日期转换,所以我假设行是通过将日期列解释为 character 来排序的。您可以在日期列上尝试strptime,例如strptime("Nov 28, 2017",format = "%b %d, %Y")。您可能需要为缩写月份名称设置正确的语言环境。
    • 谢谢大家。日期列有点棘手,但是将其从因子转换为字符,然后使用 lubridate 包将其转换为真正的日期列解决了排序部分。
    【解决方案2】:

    如果您也对 Python 中的解决方案感兴趣,请点击此处。我刚刚测试了它,它似乎可以工作:

    首先,假设列 0、1 是日期和打开,您需要将 csv 文件读入 pandas,skipinitialspace=True 只是去掉了空格。因为我们想要一个包含数据帧的列表,所以我们遍历 csv 文件:

    dfs = []
    for files in glob.glob('*.csv'):
        dfs.append(pd.read_csv('./' + files, delimiter=';', usecols=[0, 1], skipinitialspace=True))
    

    读取所有文件后,您要更改列名以使其反映文件名:

    dfs = [x.rename(columns={'Open': 'File_{}'.format(i)}) for i, x in enumerate(dfs)]
    

    现在,请到这里pandas joining multiple dataframes on columns 您可以减少以创建最终数据框:

    df_final = reduce(lambda left,right: pd.merge(left,right,on='Date', how='outer'), dfs)
    

    这将创建一个最终的数据框

    【讨论】:

    • 我刚刚发现,新列的后缀取决于列表中的位置,因此您可以检查文件是否按顺序放入列表中。就我而言,它们是......
    【解决方案3】:

    这里是使用时间序列库的另一种方法 (zoo)。

    首先我要准备一些和你类似的数据:

    library(quantmod)
    
    getSymbols(c("MSFT","FB"),src='yahoo',from='2005-01-01')
    MSFT=data.frame(Dt=strftime(time(MSFT),format = "%b %d, %Y"),
                    Close=unname(MSFT[,"MSFT.Close"]))
    rownames(MSFT)=NULL
    FB=data.frame(Dt=strftime(time(FB),format = "%b %d, %Y"),
                  Close=unname(FB[,"FB.Close"]))
    rownames(FB)=NULL
    write.csv(MSFT,file="MSFT.csv",row.names = FALSE)
    write.csv(FB,file="FB.csv",row.names = FALSE)
    

    现在来回答你的问题:

    #answer to original question
    library(zoo)
    
    filenames=c("MSFT.csv","FB.csv")
    
    finDataList=lapply(filenames,function(x) 
        read.csv.zoo(x,format="%b %d, %Y",drop=FALSE))
    
    finData=do.call("merge",finDataList)
    colnames(finData)=gsub("\\.csv","",filenames)
    head(finData)
    #             MSFT FB
    # 2005-01-03 26.74 NA
    # 2005-01-04 26.84 NA
    # 2005-01-05 26.78 NA
    # 2005-01-06 26.75 NA
    # 2005-01-07 26.67 NA
    # 2005-01-10 26.80 NA
    

    【讨论】: