像sql一样查询csv文件

像sql一样查询csv文件

这显然是一个流行的面试问题:

有 2 个包含恐龙数据的 CSV 文件。我们需要查询它们以返回满足特定条件的恐龙。

有 2 个选择 - 仅使用 Unix 命令行工具 ( /// cut) ,或者使用 Python 等脚本语言,但是pastesedawk没有额外的模块q, fsql,csvkit等等

  • 文件1.csv:
    NAME,LEG_LENGTH,DIET
    Hadrosaurus,1.2,herbivore
    Struthiomimus,0.92,omnivore
    Velociraptor,1.0,carnivore
    Triceratops,0.87,herbivore
    Euoplocephalus,1.6,herbivore
    Stegosaurus,1.40,herbivore
    Tyrannosaurus Rex,2.5,carnivore
    
  • 文件2.csv
    NAME,STRIDE_LENGTH,STANCE
    Euoplocephalus,1.87,quadrupedal
    Stegosaurus,1.90,quadrupedal
    Tyrannosaurus Rex,5.76,bipedal
    Hadrosaurus,1.4,bipedal
    Deinonychus,1.21,bipedal
    Struthiomimus,1.34,bipedal
    Velociraptor,2.72,bipedal
    
  • 使用论坛:
    speed = ((STRIDE_LENGTH / LEG_LENGTH) - 1) * SQRT(LEG_LENGTH * g)
    
    在哪里
    g = 9.8 m/s^2
    

编写一个程序来读取 csv 文件,并仅打印双足恐龙的名称,按速度从最快到最慢排序。

在 SQL 中,这很简单:

select f2.name from
file1 f1 join file2 f2 on f1.name = f2.name
where f1.stance = 'bipedal'
order by (f2.stride_length/f1.leg_length - 1)*pow(f1.leg_length*9.8,0.5) desc

如何在 Bash 或 Python 中完成此操作?

答案1

已经创建了一些工具来实现此目的。这是示例:

$ csvq 'select * from cities'
+------------+-------------+----------+
|    name    |  population |  country |
+------------+-------------+----------+
| warsaw     |  1700000    |  poland  |
| ciechanowo |  46000      |  poland  |
| berlin     |  3500000    |  germany |
+------------+-------------+----------+

$ csvq 'insert into cities values("dallas", 1, "america")'
1 record inserted on "C:\\cities.csv".
Commit: file "C:\\cities.csv" is updated.

https://github.com/mithrandie/csvq

答案2

你提到这是一个面试问题。如果我在面试中被问到这个问题,我会问有关这些限制的问题,例如,为什么我们有这些限制,什么是允许的,什么是不允许的,原因是什么。对于每个问题,我都会尝试将其与为什么我们在业务环境中存在局限性联系起来,以真正理解这里发生的事情。

另外,我想问一下动物速度公式的起源,但这只是因为我的物理科学背景比我的生命科学背景强,我对此很好奇。

作为一名面试官,我真的很想听到有用于 CSV 解析的标准工具。我会特别留意听到使用脚本或命令行实用程序从头开始解析/修改不如使用pandas和等标准工具可取csv

Stack Exchange 不适合这种类型的迭代问答,因此我将仅使用 Python 发布一个答案,只有在真正了解业务问题后,我才会在面试中提供该答案。

# Assume it's OK to import sqrt, otherwise the spirit of the problem isn't understood.
from math import sqrt

# Read data into dictionary.
dino_dict = dict()
for filename in ['file1.csv','file2.csv']:
    with open(filename) as f:
        # Read the first line as the CSV headers/labels.
        labels = f.readline().strip().split(',')

        # Read the data lines.
        for line in f.readlines():
            values = line.strip().split(',')
        
            # For each line insert the data in the dict.
            for label, value in zip(labels, values):
                if label == "NAME":
                    dino_name = value
                    if dino_name not in dino_dict:
                        dino_dict[dino_name] = dict() # New dino.
                else:
                    dino_dict[dino_name][label] = value # New attribute.

# Calculate speed and insert into dictionary.
for dino_stats in dino_dict.values():
    try:
        stride_length = float(dino_stats['STRIDE_LENGTH'])
        leg_length = float(dino_stats['LEG_LENGTH'])
    except KeyError:
        continue
    
    dino_stats["SPEED"] = ((stride_length / leg_length) - 1) * sqrt(leg_length * 9.8)
    
# Make a list of dinos with their speeds.
bipedal_dinos_with_speed = list()
for dino_name, dino_stats in dino_dict.items():
    if dino_stats.get('STANCE') == 'bipedal':
        if 'SPEED' in dino_stats:
            bipedal_dinos_with_speed.append((dino_name, dino_stats['SPEED']))

# Sort the list by speed and print the dino names.
[dino_name for dino_name, _ in sorted(bipedal_dinos_with_speed, key=lambda x: x[1], reverse=True)]

[‘霸王龙’、‘迅猛龙’、‘拟鸵鸟’、‘鸭嘴龙’]

答案3

你可以使用伟大的磨坊主并运行

mlr --csv join -j NAME -f file1.csv \
then put '$speed=($STRIDE_LENGTH/LEG_LENGTH - 1)*pow(($LEG_LENGTH*9.8),0.5)' \
then sort -nr speed \
then cut -f NAME file2.csv

获得

NAME
Tyrannosaurus Rex
Velociraptor
Euoplocephalus
Stegosaurus
Hadrosaurus
Struthiomimus

您可以通过 bash(和其他脚本语言)在几乎所有操作系统和脚本中使用它。就像剪切/粘贴/sed/awk 一样。

答案4

A gawk但在内部进行初始排序join和最终排序awk

join -t, <(sort file1.csv) <(sort file2.csv) | 
    awk -F, -v g=9.8 '/bipedal/{osaur[$1]=($4/$2-1)*sqrt(g*$2)}
        END{PROCINFO["sorted_in"]="@val_num_desc"; for (d in osaur) print d}'

Tyrannosaurus Rex
Velociraptor
Struthiomimus
Hadrosaurus

编辑@Cbhihe 评论

关于控制方式的有用资源gawk扫描数组

PROCINFO["sorted_in"]可以设置来控制读取数组的顺序。

在这种情况下,使用@value 并假设它们是numeric 并将它们排序desc为结尾,因此@val_num_desc

一个数组同样可以使用ices输出,@ind假设它们是strings并对它们进行排序asc,在这种情况下它会是@ind_str_asc

这些苍蝇的任何组合以及所有这些都在链接的资源中。

相关内容