我有一个下载多个CSV的脚本,所有CSV都有相似的名称(例如,data.csv,data(1).csv),我想向每个CSV追加一列,然后将它们组合成一个CSV。
举个例子,
data.csv有4个头文件(header_1、header_2等),我想用变量foobar添加header_5。对于data.csv,foobar是“鸭子”,因此对于data.csv的每一行,header_5都会有那么多的鸭子。
数据(1).csv是相同的,但是这个时间变量foobar现在是“狗”。该脚本将在header_5中填充任意数量的狗。
在最后一步,两个CSV将被合并--保留修改后的数据--成为一个巨大的CSV。
我已经想了很长时间了。我对Ruby了解不多,这种问题对我来说是新的,所以我希望我能很好地解释它。
我想过首先修改CSV以获得新的数据列,然后合并它们,但我一直遇到CSV名称的问题。为了让我修改这些文件,我需要知道这些文件的名称,所以我想到了通配符。但是,我如何区分CSV呢?它可能会覆盖数据。
我当前的代码就有这个问题。
def CSV_Creation (source)
input_files = Dir.glob("data*.csv")
all_headers = input_files.reduce([]) do |all_headers, file|
header_line = File.open(file, &:gets)
all_headers | CSV.parse_line(header_line)
end
CSV.open("out.csv", "a+") do |out|
all_headers << "Source"
out << all_headers
input_files.each do |file|
CSV.foreach(file, headers: true) do |row|
out << all_headers.map { |header| row[header] }
end
end
end
end
我不能完全确定如何防止最后一列中的数据不被覆盖。
编辑
感谢您到目前为止的所有回复。我已经更新了代码,希望这些代码更有意义:
def CSV_Creation (source)
l_source = {'lead_source' => "#{source}"}
input_file = Dir.glob("data*.csv").last
puts "Here is " + input_file
rows = CSV.open(input_file, headers: true).map{ |row| row.to_h }
rows.each { |h| h.merge!(l_source)}
headers = rows.first.keys
rows.first.keys.each {|k| puts k}
csv_response = CSV.generate do |csv|
csv << headers
rows.each do |row|
csv << row.values_at(*headers)
end
end
File.open("#{source}.csv", "w") {|file| file.write(csv_response)}
end
这将创建具有适当列和数据的两个不同的csv文件。现在我只需要弄清楚如何组合这两个文件。
第二次编辑
这就是最终代码的样子。它做到了我所要求的,所以我想它是正确的?
def CSV_Creation (source)
l_source = {'lead_source' => "#{source}"}
input_file = Dir.glob("data*.csv").last
puts "Here is " + input_file
rows = CSV.open(input_file, headers: true).map{ |row| row.to_h }
rows.each { |h| h.merge!(l_source)}
headers = rows.first.keys
rows.first.keys.each {|k| puts k}
csv_response = CSV.generate do |csv|
csv << headers
rows.each do |row|
csv << row.values_at(*headers)
end
end
File.open("#{source}.csv", "w") {|file| file.write(csv_response)}
input_files = Dir.glob("#{source}*.csv")
all_headers = input_files.reduce([]) do |all_headers, file|
header_line = File.open(file, &:gets)
all_headers | CSV.parse_line(header_line)
end
CSV.open("out.csv", "a+") do |out|
out << all_headers
input_files.each do |file|
CSV.foreach(file, headers: true) do |row|
out << all_headers.map { |header| row[header] }
end
end
end
end
非常感谢给我建议的每一个人!
发布于 2019-06-19 21:43:59
我有个愚蠢的方法可以按你的要求去做:
中的列来自哪个文件
# idk what to do with source
def CSV_Creation (source)
input_files = Dir.glob("data*.csv").map { |filename| File.open(filename) }
headers = input_files.map(&:gets)
# Fix for "empty" lines in data files
line_fix = headers.map { |header| CSV.parse_line(header).map { ',' }.join }
CSV.open("out.csv", "a+") do |out|
# We add the header
out.puts headers.map(&:chomp).join(',')
# We try to read all the lines
until (lines = input_files.map(&:gets)).concat.empty?
out.puts lines.map.with_index do |line, index|
line&.chomp || line_fix[index]
end.join(',')
end
end
# In order to know the names we'll store a csv associating header to the filename
File.open('source.csv', 'w') do |f|
f.puts headers.map(&:chomp).join(',')
line = input_files.map.with_index do |file, index|
([file.path] * line_fix[index].size).to_csv
end
f.puts line.map(&:chomp).join(',')
end
ensure
input_files.each(&:close)
end
发布于 2019-06-20 08:34:26
代码
require 'csv'
def combine_csv_files(*csv_files, sep, out_file_name)
(file_name, new_header_name), *rest = csv_files
csv = CSV.read(file_name, headers: true, col_sep: sep)
new_col = (1..csv.size).to_a
csv[new_header_name] = new_col
rest.each do |file_name, new_header_name|
csv1 = CSV.read(file_name, headers: true, col_sep: sep)
csv1.headers.each { |header| csv1.each { |row| csv[header] = row[header] } }
csv[new_header_name] = new_col
end
CSV.open(out_file_name, "w") do |f|
f << csv.headers
csv.each { |row| f << row }
end
end
示例
假设我们有文件
FNAME1 = 'dogsandcats.csv'
FNAME2 = 'cowsandpigs.csv'
其内容与我的另一个答案中给出的内容相同,我们希望向这两个文件中分别添加标题为"col1"
和"col2"
的列,并将这两个文件组合在一起。
combine_csv_files(*[[FNAME1, "col1"], [FNAME2, "col2"]], ',', 'everything.csv')
puts IO.read('everything.csv')
dog,cat,col1,cow,pig,col2
woof,purr,1,moo,oink,1
devoted,independent,2,dumb,smart,2
说明
步骤如下(对于示例中定义的FNAME1
和FNAME2
)。
csv_files = [[FNAME1, "col1"], [FNAME2, "col2"]]
sep = ','
out_file_name = 'everything.csv'
在第一个元素和所有剩余元素之间拆分csv_files
。
(file_name, new_header_name), *rest = csv_files
#=> [["dogsandcats.csv", "col1"], ["cowsandpigs.csv", "col2"]]
file_name
#=> "dogsandcats.csv"
new_header_name
#=> "col1"
rest
#=> [["cowsandpigs.csv", "col2"]]
以这种方式划分csv_files
的过程称为array decomposition。现在读取第一个文件,创建一个CSV
对象。
csv = CSV.read(file_name, headers: true, col_sep: sep)
#=> #<CSV::Table mode:col_or_row row_count:3>
让我们看看我们有什么。
csv.to_a
#=> [["dog", "cat"], ["woof", "purr"], ["devoted", "independent"]]
现在添加一列,看看我们现在有什么。
new_col = (1..csv.size).to_a
csv[new_header_name] = new_col
#=> [1, 2]
csv.to_a
#=> [["dog", "cat", "col1"], ["woof", "purr", 1], ["devoted", "independent", 2]]
读取下一个CSV文件的描述符,然后将该文件读取到CSV
对象csv1
中
file_name, new_header_name = rest.shift
#=> ["cowsandpigs.csv", "col2"]
csv1 = CSV.read(file_name, headers: true, col_sep: sep)
#=> #<CSV::Table mode:col_or_row row_count:3>
csv1.to_a
#=> [["cow", "pig"], ["moo", "oink"], ["dumb", "smart"]]
将csv1
追加到csv
csv1.headers.each { |header| csv[header] = csv1.map { |row| row[header] } }
#=> ["cow", "pig"]
csv.to_a
#=> [["dog", "cat", "col1", "cow", "pig" ],
# ["woof", "purr", 1, "moo", "oink" ],
# ["devoted", "independent", 2, "dumb", "smart"]]
添加新列并检查csv
csv[new_header_name] = new_col
#=> [1, 2]
csv.to_a
#=> [["dog", "cat", "col1", "cow", "pig", "col2"],
# ["woof", "purr", 1, "moo", "oink", 1 ],
# ["devoted", "independent", 2, "dumb", "smart", 2 ]]
剩下的工作就是将csv
写到文件中。
CSV.open(out_file_name, "w") do |f|
f << csv.headers
csv.each { |row| f << row }
end
#> #<CSV::Table mode:col_or_row row_count:3>
让我们看一下刚刚写入的文件的内容:
puts IO.read(out_file_name)
dog,cat,col1,cow,pig,col2
woof,purr,1,moo,oink,1
devoted,independent,2,dumb,smart,2
https://stackoverflow.com/questions/56668220
复制相似问题