【问题标题】:Combine 2 CSV files based on a match within a column disregarding the header row根据列中的匹配组合 2 个 CSV 文件,而忽略标题行
【发布时间】:2016-06-18 17:54:59
【问题描述】:

我一直在寻找这个解决方案的 ole interweb,但没有发现任何成功的东西。我有一个来自一个脚本的 CSV 输出,该脚本具有以特定方式呈现的数据,我需要匹配它并与另一个文件合并。如果我可以四舍五入到一个简单的 2 x 小数点,则额外奖励。

文件 1:dataset1.csv(使用第 1 列作为主键或我想在其他文件中搜索的内容。)

5033db62b38f86605f0baeccae5e6cbc,20.875,20.625,41.5
5033d9951846c1841437b437f5a97f0a,3.3529411764705882,12.4117647058823529,13.7647058823529412
50335ab3ab5411f88b77900736338bc6,6.625,1.0625,3
5033db62b38f86605f0baeccae5e6cbc,2.9375,1,1.4375

文件 2:dataset2.csv(如果第 2 列匹配文件第 1 列的第 1 列,则从文件 2 替换文件 1 第 1 列中的数据。)

"dc2","5033db62b38f86605f0baeccae5e6cbc"
"dc1","5033d9951846c1841437b437f5a97f0a"

期望的结果:

文件 1(或新文件 3):

dc1,3.35,12.41,13.76
dc2,20.875,20.625,41.5

只是为了证明我一直在努力寻找方法,而不是随便问一个问题,希望别人能解决我的问题。

我发现了许多说要使用联接的资源。

join -o 1.1,1.2,1.3,1.4,2.3 file 1 file 2 等。我已经测试了很多不同的方法。我在许多帖子上读到需要对结果进行排序 - 这么长的字符串有点难。更不用说文件 1 可能有 30 到 40 个条目,但文件 2 可能只有 10 个。我只需要一个与长字符串关联的名称。

我开始研究 grep - 但是我需要一个 forEach 循环来循环遍历所有结果,并且必须有一个更简单的方法。

我还研究了 AWK - 现在这是一个有趣的尝试,试图弄清楚如何使这项工作发挥作用。

awk 'FNR==NR {a[$2]; next} $2 in a' file.csv testfile2.csv

是的....尝试了很多方法来比较这个,因为这似乎是一般的想法......但仍然没有让它起作用。我希望这是某种类型的 linux shell 脚本,非常简单,我可以从 php 页面调用并运行它。就像用户点击刷新一样,它会搅动它并消化数据。

任何帮助将不胜感激!

谢谢。

j。

【问题讨论】:

  • 您不介意答案基于哪种语言? (PHP、python、awk)?
  • 我正在探索所有选项 - 它必须简单且易于重复......我仍在测试以下一些建议。

标签: php python csv awk merge


【解决方案1】:

您可以结合使用 sort 和 gnu awk:

mergef.awk:

BEGIN   { FS= "[ ,\"]+"; }
FNR == NR { if ( !($1 in vals) ) vals [ $1 ] = sprintf("%.2f,%.2f,%.2f", $2, $3,$4) ;}
FNR != NR { print $2 "," vals[ $3 ]; }

假设你的文件是 f1.csv 和 f2.csv 然后使用这个命令:

awk -f mergef.awk f1.csv f2.csv | sort
  • 脚本中的第一行处理第二个文件中存在的引号(由于此设置,第二个文件有一个空字段$1
  • 第二行读入第一个文件。 if 注意只使用第一次出现的密钥。
  • 最后一行打印第二个文件中的键以及第一个文件中存储的值,通过旧键检索
  • FNR == NR 对第一个文件为真

【讨论】:

  • 效果很好!谢谢!附加问题我将如何删除那些没有匹配记录的?这是在您的脚本的帮助下 - 12k, small-4K, small-Gen5,804.12,173.88,977.35 small-2K, small-1,我想删除那些没有任何附加记录的。
  • @Jared 这些记录在哪里?它们在第二个文件中吗?因为在第二个文件中没有配对的第一个文件中的 rowd 已经被删除了。
  • @Jared 要从第二个文件中删除行而不在第一个文件中配对,请将脚本的最后一行更改为 FNR != NR { if( $3 in vals) print $2 "," vals[ $3 ]; }
  • 基本上在文件 2 中有 dc1 和数字字符串 ... 如果文件 1 中没有匹配项,则删除不匹配的项。本质上,如果文件 1 中的数据在文件 2 中不存在,那么我在输出中不需要它。
  • 还修改了最后一行,在输出中添加了一些错误...它抓取了输出中的标题并将其与某些内容匹配。 awk -f mergef.awk file1.csv file2.csv | sort 7NA_01,0.00,0.00,0.00 Name,0.00,0.00,0.00 -->> 这是file2 dc1,0.69,1.00,1.00 dc2,1.92,1.04,1.31中的标题
【解决方案2】:

使用pythonpandas 库:

import pandas as pd

# Read in the csv files.
df1 = pd.read_csv(dataset1.csv, header=None, index_col=0)
df2 = pd.read_csv(dataset2.csv, header=None, index_col=1)

# Round values in the first file to two decimal places.
df1 = df1.round(2)

# Merge the two files.
df3 = pd.merge(df2, df1, how='inner', left_index=True, right_index=True)

# Write the output.
df3.to_csv(output.csv, index=False, header=False)

【讨论】:

    【解决方案3】:

    除了格式化数字之外

    $ join -t, -1 1 -2 2 -o2.1,1.2,1.3,1.4 <(sort file1) <(tr -d '"' <file2 | sort -t, -k2)
    
    dc1,3.3529411764705882,12.4117647058823529,13.7647058823529412
    dc2,2.9375,1,1.4375
    dc2,20.875,20.625,41.5
    

    请注意,dc2 有两个匹配项。

    奖励:对于所需的格式化管道,前一个脚本的输出到

    $ ... | tr ',' ' ' | xargs printf "%s,%.2f,%.2f,%.2f\n"
    
    dc1,3.35,12.41,13.76
    dc2,2.94,1.00,1.44
    dc2,20.88,20.62,41.50
    

    但是,也许awk 是一个更好的选择。这是为了表明,如果您可以利用现有的 unix 工具集,则无需编程。

    【讨论】:

      【解决方案4】:

      这是一个使用 PHP 的解决方案:

      foreach (file("dataset1.csv") as $line_no => $csv) {
          if (!$line_no) continue; // in case you have a header on first line
          $fields = str_getcsv($csv);
          $key = array_shift($fields);
          $data1[$key] = array_map(function ($v) { return number_format($v, 2); }, $fields);
      };
      
      foreach (file("dataset2.csv") as $csv) {
          $fields = str_getcsv($csv);
          if (!isset($data1[$fields[1]])) continue;
          $data2[$fields[0]] = array_merge(array($fields[0]), $data1[$fields[1]]);
      };
      
      ksort($data2);
      
      $csv = implode("\n", array_map(function ($v) {
          return implode(',', $v);
      }, $data2));
      
      file_put_contents("dataset3.csv", $csv);
      

      注意:正如您提到的第一个文件将使用第 1 列作为主键,不应出现重复的键值。如果是,则以最后出现的为准。

      【讨论】:

      • 有了这个 - 有没有办法删除没有匹配字符串的记录?
      • 是的,我刚刚添加了一行代码(使用isset)来跳过第二个文件中与第一个文件中的键不匹配的条目。
      • 7NA_01,0.00,0.00,0.00 名称,,, 更正不是它输出到文件的错误,而是很像上面的 awk 示例,它的名称与某些东西匹配。
      • 我正在努力理解您刚刚写的内容...您能澄清一下您提出的问题吗?
      • 我不好,我编辑了它,它删除了我拥有的所有其他东西。我以为我有一个错误,但我能够让它工作。您的脚本的输出甚至它上面的脚本都将其中一条记录与标题匹配。这就是你在那里看到的名字。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-25
      • 2019-12-01
      • 2015-01-30
      相关资源
      最近更新 更多