【问题标题】:Aggregate CSV data based on unique ID using command-line tools [closed]使用命令行工具基于唯一 ID 聚合 CSV 数据 [关闭]
【发布时间】:2021-05-05 00:29:11
【问题描述】:

我想使用命令行工具(例如bashsedawk 等)将数据从一个 CSV 文件聚合到另一个文件中。每个文件中的每一行都以第 1 列中的唯一 ID 开头,如果目标中的一行与此 ID 匹配,则应替换该行,否则应附加该行。输入数据未排序,但结果的排序顺序无关紧要,因此如果有帮助,可以将文件作为脚本的一部分进行排序。

例如,给定aggregate.csv的当前状态:

1,olddata
3,olddata
2,olddata

还有文件new.csv

4,newdata
2,changeddata
3,changeddata

我希望aggregate.csv 出现如下(按任何排序顺序):

1,olddata
2,changeddata
3,changeddata
4,newdata

这些行可能包含大量列,因此一次替换一个单元格并不理想。 CSV 保证不包含带引号的换行符,因此逐行搜索和一次替换整行是一种有效的方法。

抱歉,如果这是重复的,但我找不到另一个完全使用这种 CSV 合并方法的问题。

我已尝试将答案调整为this question,但它需要首先通过逐行解析两个文件、排序、删除重复项和保存来生成所有 ID 的“模板文件” - 我希望有一个更简单的方法是可能的。

This question 在 sed 和 awk 中有答案,我也复制了这些答案,并管理了 regex-replacement 部分,但不是在不存在匹配项时向文件追加新行的方法。

谢谢!

【问题讨论】:

  • 需要了解正在处理的数据量,以确定(简单的)内存解决方案是否可行,或者我们是否需要查看(更)复杂的解决方案以解决潜在的内存不足问题- 记忆问题;每个文件有多少行?对这些线的最大宽度有一个大概的数字吗?回复:带有 'a large number of columns' 的行 ...您是要替换整行还是仅替换某些单元格?源文件和目标文件是否具有相同数量的单元格/字段?
  • @markp-fuso 粗略地说,我正在测试的文件有大约 30000 行,每个包含 20 个字段和大约 300 个字符。 (我想这可能不是每个人都会考虑的“大量列”,但我想避免必须在 20 个字段上执行 20 次任务的解决方案。)我正在寻找替换整行。源文件和目标文件的列数相同,但行数不同。
  • 所以最大文件大小约为 9MB(给或取几 MB)?替换全部或部分行?
  • 这是一个愿望,而不是关于你已经做过的任何事情的问题。
  • 用你的一些(编码)尝试来更新问题并没有什么坏处;然后我们可以就(代码)更正提出建议

标签: bash join awk sed command-line


【解决方案1】:

假设:

  • 替换整行
  • 总文件大小约为 8-10 MB(因此可能不会出现任何内存不足问题)
  • 字段 #1 在每个文件中都是唯一的
  • 输出没有排序要求
  • 输入不保证排序

一个awk解决方案:

awk -F"," '                      # input delimiter is comma ","

FNR==NR { a[$1]=$0 ; next }      # first file: store contents in array a[], using field #1 as index

        { if ( $1 in a )         # second file: if field #1 is an index in array a[] then
             { print a[$1]       # print the contents of said array entry and
               delete a[$1]      # remove entry from array
             }
          else                   # field #1 is not an index in array a[] so
             print $0            # print current line
        }

END     { for ( i in a )         # anything still in array a[] was not in second file so
              print a[i]         # print these entries
        }
' new.csv aggregate.csv          # order of input files is important !!!

这会产生:

1,olddata
3,changeddata
2,changeddata
4,newdata

注意:虽然输出似乎按字段 #1 排序,但这只是基于输入数据排序的巧合; awk 代码中没有任何内容明确生成此“有序”输出。

这将不会覆盖aggregate.csv 文件。虽然可以使用GNU awk-i inplace 选项覆盖aggregate.csv,但这需要更改处理以防止覆盖new.csv。最简单的解决方案可能是将此输出定向到一个新文件,然后 OP 可以决定是否应该使用所述新文件覆盖aggregate.csv

【讨论】:

  • 太棒了,也感谢您提供完整评论的解决方案 - 对我将来更好地理解 awk 非常有帮助!
【解决方案2】:

使用任何 awk:

$ awk -F, '!seen[$1]++' new agg
4,newdata
2,changeddata
3,changeddata
1,olddata

或使用 GNU 排序 -s:

$ sort -ust, -k1,1 new agg
1,olddata
2,changeddata
3,changeddata
4,newdata

【讨论】:

  • 另一个很好的答案,谢谢!我不确定将哪一个标记为已接受 - 一个是因为它的简单性,另一个是因为它的解释性 cmets,但两者似乎都有效。
  • 你都懂吗?如果没有,请随时提出问题。如果是这样,那么您将在代码中使用哪一个?一般来说,你不应该只接受你得到的第一个答案,因为从给定的样本输入集中产生预期输出的答案是确定最佳(甚至是正确)解决方案的起点,而不是终点,并接受任何答案都会阻止其他人发布替代答案,因此这是您的损失。通常最好等待几个小时或一天,看看你得到什么答案,然后接受一个。
  • 公平点!我确实理解您的答案,现在我已经阅读了更多内容。我以前没有遇到过用于 awk 的“seen”命令,而且我也没有意识到带有 -k 标志的排序在测试唯一性时只会考虑该字段,而不仅仅是确定排序顺序。事后看来很明显!
  • Ian @markp-fuso 是正确的,我可以将该数组命名为 fluffybunny[],并且代码的行为方式相同,我们只是习惯性地将数组命名为 seen[],当它被用来告诉它的索引的第一次出现来自该同一索引的任何后续出现。使用该成语的在线脚本必须有数千个,请参阅stackoverflow.com/questions/57040414/…了解更多信息。
  • 啊好吧,所以 '!seen[$1]++' 就足以说明“如果数组中不存在字段 1,则将其添加到数组中并继续处理,但如果确实如此,请停止处理这一行”,而没有任何关于“看到”的特别之处。明白了,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-27
  • 1970-01-01
  • 1970-01-01
  • 2015-05-28
  • 2021-05-12
  • 2012-03-09
  • 2015-02-13
相关资源
最近更新 更多