放弃Excel并使用Julia数据框

2020-11-27 00:43:08

可以在电子表格应用程序(例如Excel)中使用诸如R,Python(Pandas)和Julia(DataFrames.jl)等流行的数据框架来处理此类数据。

首先,我们将数据加载到Julia中,并选择表中列的子集(id,名称,大小和价格)以进行处理:

使用DataFrames,CSV url =“ https://vincentarelbundock.github.io/Rdatasets/csv/gt/pizzaplace.csv”文件名=下载(url)all_pizzas = CSV.read(文件名)#删除行号为all_pizzas的列= all_pizzas [:, 2:end]#选择最有趣的列pz = select(all_pizzas,:id,:name,:size,:price)

我们可以查看第一个视图行,以了解Julia REPL(读取评估程序循环)的样子:

julia> first(pz,4)4×4 DataFrame│行│id│名称│大小│价格│││字符串│字符串│字符串│Float64│├─────┼──────── ────┼────────────────────────││1│2015-000001│夏威夷人│M│13.25│ │2│2015-000002│classic_dlx│M│16.0││3│2015-000002│mexicana│M│16.0│││4│2015-000002│thai_ckn│L│20.75│julia> nrow(pz)49574

但是,我们目前正在查看前4行。但是正如您所看到的,该数据集中几乎有5万行,因此粘贴到电子表格中不是很实际。同样出于教育原因,将选择较小的子集。

我们将从加载的49 574行中随机抽取16行样本。为此,我们将从1到49 574的行索引中随机排序。

然后,我们可以选择这些混排的前16行,以从原始数据中随机获取16行:

julia> sample = pz [rows [1:16],:] 16×4 DataFrame│行│id│名称│大小│价格│││字符串│字符串│字符串│Float64│├─────┼─── ──────────┼──────────┤│1│2015- 000348│thai_ckn│S│12.75││2│2015-007731│绿色花园│S│12.0││3│2015-014409│夏威夷│S│10.5││4│2015-017919│西西里人│S│12.25││5│ 2015-015337│prsc_argla│M│16.5││6│2015-006190│ital_veggie│S│12.75││7│2015-015481│spin_pesto│S│12.5││8│2015-007865│夏威夷语│L│16.5││ 9│2015-001928│bbq_ckn│L│20.75││10│2015-017298│cali_ckn│S│12.75││11│2015-018872│four_cheese│L│17.95│││12│2015-018036│four_cheese│L│17.95 │13 │12.0│

您将方括号中使用的语法读为[行,列],其中行是所需行的集合。这可以是范围,向量或单个标量值。列也是如此。

但是,我们无法轻松地将这种“漂亮”格式的数据复制并粘贴到电子表格应用程序(例如Excel)中。我们希望它采用CSV格式。幸运的是,Julia显示系统使我们能够以许多不同的格式显示相同的数据。

在Julia REPL中,实际上自动发生的是显示函数的调用方式如下:

julia> display(sample)16×4 DataFrame│行│id│名称│大小│价格│││字符串│字符串│字符串│字符串│Float64│├───┼────────── ──┼──────────────┼│1│2015-000348│thai_ckn│S│12.75││ 2│2015-007731│绿色花园│S│12.0││3│2015-014409│夏威夷人│S│10.5││4│2015-017919│西西里人│S│12.25│││5│2015-015337│prsc_argla│M│16.5 ││6│2015-006190│ital_veggie│S│12.75││7│2015-015481│spin_pesto│S│12.5││8│2015-007865│夏威夷语│L│16.5│││9│2015-001928│bbq_ckn│L │20.75││10│2015-017298│cali_ckn│S│12.75││11│2015-018872│fourcheese│L│17.95│││12│2015-018036│four_cheese│L│17.95│││13│2015-011238│classic_dlx │L│20.5││14│2015-013366│classic_dlx│M│16.0│││15│2015-014380│bbq_ckn│M│16.75│││16│2015-020245│ital_cpcllo│S│12.0│

但是,此函数也可以将MIME类型用作参数,例如在Julia在支持更丰富的图形功能的笔记本中使用。在笔记本中,我们提供text / html MIME类型,但是在这种情况下,我们希望数据为CSV格式:

您可以将其复制并粘贴到电子表格应用程序中。但是有很多方法可以做到这一点。我们可以使用CSV包写入文件:

之后,比萨销​​售数据将显示在剪贴板上,您可以将其粘贴到您喜欢的电子表格应用程序中。

而不是拿!我们本可以使用seekstart转到IOBuffer的开头并正常读取它。但是使用take的好处!是您清空了缓冲区,因此它不再占用任何内存来保存CSV数据。

最终,您将在首选的电子表格应用程序中获得如下表所示的表格,无论是Excel,Numbers还是Google表格。电子表格中有用的是您可以单击一列并获取各种统计信息。注意我们如何获得总和,平均值,最小值和最大值。

在Julia中获取相同内容很容易。我们只需要导入统计信息包即可。

在进行探索性数据分析(EDA)时,您可以做的第一件事就是了解数据,即在直方图中绘制有趣的数据。

我们可以将样本数据的直方图与整个数据集的相似直方图进行比较:

我们可以合并图并将它们并排。但是在比较图时,最好将数据放在相似的范围内。因此,我们将使用xaxis和yaxis属性指定范围。

julia> p1 =直方图(sample.price,bins = 4,xlims =(0,40),xaxis =“ price”,yaxis =“ count”,图例=没有东西)julia> p2 =直方图(pz.price,bins = 4,xlims =(0,40),xaxis =“价格”,yaxis =“计数”,图例=没有内容julia> p = plot(p1,p2)

如果要将图包括在报告和文档中,可以使用savefig函数将其保存到磁盘。它将根据您的文件扩展名选择所需的格式。支持.png,.pdf,.svg和其他几种格式:

查看数据后,我们可能会提出一些有关我们要回答的数据的问题,例如:

许多此类数据无法直接从数据中拉出。我们将不得不对相关数据进行分组。例如。为了弄清楚每个订单花费了多少,我们必须将属于同一订单的每一行分组,并合计该订单中每个披萨的销售价格。

为了找出哪个比萨饼能带来最多的收入,我们必须按比萨饼名称对行进行分组,并对每行的价格进行汇总。

因此,为了能够回答此类问题,我们需要更多地了解如何在DataFrame中过滤,分组和组合数据。

使用数据框,我们获得了一些关键功能来帮助我们处理数据:

选择拾取列的子集,并可能重命名或转换列中的值。

类似于select,只是不删除任何列。我们只是重命名和转换选定的列。

groupby将一个表转换为多个表。通过为特定选定列的每个唯一值创建一个表来完成拆分。

合并提取多张表,然后再次将它们变成一张表。允许您将每个表中的所有行折叠为单行。

join让您匹配两个不同表中的一列,以将它们连接到一个表中。

对于使用过SQL的任何人,其中许多操作都是熟悉的。这些函数使用相似的语法选择,重命名和转换行。

julia> tiny = first(sample,3)3×4 DataFrame│行│id│名称│大小│价格│││字符串│字符串│字符串│Float64│├─────┼────── ││1│2015-000348│thai_ckn│S │12.75││2│2015-007731│绿园│S│12.0│││3│2015-014409│夏威夷│S│10.5│

julia> select(tiny,:price)3×1 DataFrame│行│价格│││Float64│├─────┼───────────││1│12.75│││2│12.0││ 3│10.5│julia> transform(tiny,:price)3×4 DataFrame│行│id│名称│大小│价格│││字符串│字符串│字符串│Float64│├──────┼────── ────────┼──────────┼│1│2015-000348│ thai_ckn│S│12.75││2│2015-007731│绿色花园│S│12.0│││3│2015-014409│夏威夷│S│10.5│

对于转换,此操作是毫无意义的,因为您得到的行与开始时相同。通过重命名,它变得毫无意义,因为您最终只是添加了一个与您重命名的列具有相同值的列。

julia> select(tiny,:price =>:cost)3×1 DataFrame│行│成本│││Float64│├─────┼─────────┤│1│12.75│││2 │12.0││3│10.5│julia> transform(tiny,:price =>:cost)3×5 DataFrame│行│id│名称│大小│价格│成本││││字符串│字符串│字符串│Float64│Float64│ ├─────┼────────────┼┼──────────┼ ──────────────────│1│2015-000348│thai_ckn│S│12.75│12.75│││2│2015-007731│green_garden│S│12.0│12.0││3│2015- 014409│夏威夷人│S│10.5│10.5│

julia> select(tiny,:price => mean)3×1 DataFrame│行│price_mean│││Float64│├─────┼──────────││1│11.75│ │2│11.75││3│11.75│julia> transform(tiny,:price => mean)3×5 DataFrame│行│id│名称│大小│价格│price_mean│││字符串│字符串│字符串│Float64│Float64 │├─────┼──────────────┼──────────┼ ──────┼────────────┤│1│2015-000348│thai_ckn│S│12.75│11.75││2│2015-007731│green_garden│S│12.0│11.75│││ 3│2015-014409│夏威夷人│S│10.5│11.75│

但是,这些转换毫无意义,因为我们最终得到的每一行的平均值相同。原因是所提供的函数始终以整列为参数,并且需要为整列吐出一个或多个值。平均值仅给出一个输出值。

如果我们想将函数应用于每个值,则可以使用ByRow轻松生成这样的函数。这是一个演示其工作原理的简单示例:

因此,ByRow为我们提供了创建可以在每一行上运行的函数的简单方法:

julia> select(tiny,:price => ByRow(round))3×1 DataFrame│行│price_round│││Float64│├────────────────────│ 1│13.0│││2│12.0││3│10.0│julia> transform(tiny,:price => ByRow(round))3×5 DataFrame│行│id│名称│大小│价格│price_round││││字符串│字符串│字符串│Float64│Float64│├─────┼───────────── ──┼──────────┼────────┤│1│2015-000348│thai_ckn│S│12.75│13.0││2│2015-007731│green_garden │S│12.0│12.0││3│2015-014409│夏威夷语│S│10.5│10.0│

julia> transform(tiny,:price => ByRow(round)=>:rounded)3×5数据框│行│id│名称│大小│价格│舍入│││字符串│字符串│字符串│字符串│Float64│Float64│├─ ──────────────────┼────────┼ │┼──────────┤│1│2015-000348│Thai_ckn│S│12.75│13.0│││2│2015-007731│green_garden│S│12.0│12.0││3│2015-014409│夏威夷语│S│10.5│10.0│

但这是否意味着在没有ByRow的情况下使用函数毫无意义?不,实际上与结合使用时是非常有用的,因为它的工作原理相似,当选择每列由取整列功能转化,除了选择并返回一个值,如意味着你将结束与一个单一的行。

julia> Combine(tiny,:price => mean)1×1 DataFrame│行│price_mean│││Float64│├─────┼──────────││1│11.75│

但是,如果您不这样做,则合并将基本上具有与选择相同的行为:

julia> Combine(tiny,:name,:price => mean)3×2 DataFrame│行│名称│price_mean││││字符串│Float64│├─────┼──────── ────┼────────────┤│1│Thai_ckn│11.75││2│green_garden│11.75││3│夏威夷│11.75│

但是,合并的真正功能是使用groupby将数据帧分成几组,然后再次将它们重新组合到表中。

诀窍是将它们与带多个元素并产生单个输出的函数一起使用,例如:

让我们用它来计算每种大小的披萨数量:

julia>大小= groupby(sample,:size)基于密钥的3组GroupedDataFrame:大小组1(7行):大小=“ M”│行│id│名称│大小│价格││││字符串│字符串│字符串│Float64│├──────┼───────────┼──────────┼ ────────┤│1│2015-018649│意大利辣香肠│M│12.5││2│2015-011258│夏威夷语│M│13.25│││3│2015-009299│素食主义者│M│16.0│││4│ 2015-010260│peppr_salami│M│16.5││5│2015-017124│夏威夷│M│13.25││6│2015-011800│thai_ckn│M│16.75│││7│2015-008107│ckn_alfredo│M│16.75│ 2(5行):size =“ L”│行│id│名称│名称│大小│价格│││字符串│字符串│字符串│Float64│├─────┼──────── ──┼────────────┼│1│2015-000629│spin_pesto│L│20.75│││ 2│2015-011532│spinach_fet│L│20.25││3│2015-019947│意大利辣香肠│L│15.25││4│2015-002630│Thai_ckn│L│20.75│││5│2015-018629│cali_ckn│L│20.75 │第3组(4行):大小=“ S”│行│id│名称│大小│价格│││弦线│弦线│弦线│Float64│├─────┼───────────┼ ──────┼──────────┤│1│2015-017814│green_garden│S│12.0│││2│2015-012022│veggie_veg│S│12.0│││3│2015-010260│southw_ckn │S│12.75││4│2015-010846│大肉│S│12.0│

size不是由GroupedDataFrame类型表示的组的集合。我们可以重新组合这些组:

julia> Combine(sizes::size => length)3×2 DataFrame│行│大小│size_length│││字符串│Int64│├─────┼──────┼ ────────┤│1│M│7││2│L│5││3│S│4│

julia> Combine(sizes,:size => length =>:amount)3×2 DataFrame│行│大小│数量│││字符串│Int64│├─────┼────── ────────┤│1│M│7││2│L│5││3│S│4│

您会看到此绘图的一个问题是,尺寸没有按S,M,L,XL,XXL顺序排序,这使它变得混乱。我们该如何解决?

有很多方法可以定义披萨尺寸的订单,但是在这里,我将通过连接表格来解决此问题,因为无论如何这是您应该知道的事情。一个简单的解决方案是给尺寸一个数值。

我们将对XXL使用超大尺寸。让我们制作一张表格,将比萨饼的尺寸映射到直径:

julia>直径= DataFrame(大小= [“ S”,“ M”,“ L”,“ XL”,“ XXL”],直径= [26、31、36、41、46])5×2 DataFrame│行│大小│直径│││字符串│Int64│├─────┼────────┼│1│S│26││2│M│ 31││3│L│36││4│XL│41││5│XXL│46│

要加入,我们需要指定哪一列将成为主键或我们将加入的值。这将是大小,因为这是两个表中唯一的列:

julia> pz = join(pz,直径,on =:大小); julia> select(pz,:name,:size,:diameter,:price)49574×4 DataFrame│行│名称│大小│直径│价格│价格│字符串│字符串│Int64│Float64│├────── ──┼────────────┼──────│1│ spin_pesto│L│36│20.75││2│意大利辣香肠│M│31│12.5│││3│夏威夷人│M│31│13.25│││4│spinach_fet│L│36│20.25││││5│素食主义者│M│31│ 16.0│

但是,让我们回到原来的任务。我们要在表中插入直径,以说明出售的比萨饼有多少种是特定类型的:

julia> pz_sizes = Combine(groupby(pz,:size),:size => length =>:amount)julia> pz_sizes = join(pz_sizes,diameters,on =:size)5×3 DataFrame│行│大小│数量│直径│││字符串│Int64│Int64│├─────┼──────────┼│1│M │15635│31││2│L│18956│36││3│S│14403│26││4│XL│552│41││5│XXL│28│46│

julia> sort!(pz_sizes,:diameter)5×3 DataFrame│行│大小│数量│直径│││字符串│Int64│Int64│├─────┼──────┼── ──────┼──────────┤│1│S│14403│26││2│M│15635│31││3│L│18956│36││4│XL│552 │41││5│XXL│28│46│

绘图当然是一门艺术。为了了解主导销售的因素,使用饼图可能更有意义:

查看我们的图表,我们可以看到售出的大多数比萨饼都是大的,中等的仅次于比萨饼。但是,就吃的披萨片而言,它的外观如何?

基本上,我们想知道的是哪种比萨饼消费最多。因为我们知道直径,所以我们可以计算每个披萨的面积。

为了使显示更好一点,我将四舍五入计算出的面积:

julia> pz_sizes.area =圆形(int,pz_sizes.area)julia> pz_sizes 5×4 DataFrame│行│大小│数量│直径│面积│││字符串│Int64│Int64│Int64│├─────┼ ────────┼────────────────────────────────────││S│14403│26│531││2 │M│15635│31│755││3│L│18956│36│1018│││4│XL│552│41│1320││││5│XXL│28│46│1662│

julia> pzs = pz_sizes julia> pzs.total_area = pzs.area。* pzs.amount julia> pzs 5×5 DataFrame│行│大小│数量│直径│面积│total_area││││字符串│Int64│Int64│Int64│Int64 │├─────┼────────┼──────┼──────┼ ────────┤│1│S│14403│26│531│7647993││2│M│15635│31│755│11804425│││3│L│18956│36│1018│19297208││4│ XL│552│41│1320│728640│││5│XXL│28│46│1662│46536│

让我们通过绘制已购买的披萨数量并将其与每种类型的披萨消费面积进行比较来使其更加有趣:

p1 =饼图(pzs.size,pzs.amount,title =“数字披萨”); p2 =饼图(pzs.size,pzs.total_area,title =“按区域划分的披萨”); p =情节(p1,p2)

然后,我们可以将每个同名比萨的价格相加。

但是我们希望将其分类,以便我们可以找到销量最高的10个披萨。

朱莉娅>披萨= groupby(pz,:name);茱莉亚>比萨=组合(比萨,:价格=>总和=>:收入); julia> top10 = first(sort(pizzas,:revenue,rev = true),10)10×2 DataFrame│行│名称│收入││字符串│Float64│├─────┼────── ────────┼──────────┤│1│Thai_ckn│43434.2││2│bbq_ckn│42768.0│││3│cali_ckn│41409.5││4│classic_dlx│38180.5│││5│辣│34831.2││6│southw_ckn│34705.8││7│ital_supr│33476.8││8│夏威夷文│32273.2│││9│四芝士│32265.7│││10│西西里人│30940.5│

为了获取每个比萨饼订单的花费,我们为每个唯一的订单ID创建数据框的子组:

julia> groupby(pz,:id)具有基于密钥的21350个组的GroupedDataFrame:id第一组(1行):id =“ 2015-000001”│行│id│名称│大小│价格│直径│直径│面积│││字符串│字符串│字符串│Float64│Float64│Float64│├───┼───────────┼ ──┼──────────┼────────┤│1│2015-000001│夏威夷语│M│13.25│0.31│0.0755│ ⋮最后一组(1行):id =“ 2015-021350”│行│id│名称│尺寸│价格│直径│面积│││字符串│字符串│字符串│Float64│Float64│Float64│├───── ────────────┼─────────┼──────┼──────┼ ──────────────────│1│2015-021350│bbq_ckn│S│12.75│0.26│0.0531│

然后,我们可以通过汇总价格列来重新组合这些组,这使我们可以将每个子组折叠到新表的单个行中。在此表中,每个订单只有一行:

julia>订单=组合(groupby(pz,:id),:price => sum =>:sum)21350×2 DataFrame│行│id│sum│││字符串│Float64│├───────┼ ──────────────┼────────────────────────────────││1│2015-000001│13.25││2│2015-000002│92.0│​​│3│2015-000003│37.25 ││4│2015-000004│16.5│

首先让我们对这些数据进行一些简单的探索。查看最小值,最大值,均值和中位数始终是一件好事。

julia> describe(orders,:min,:max,:mean,:median)2×5数据框│行│变量│最小│最大│平均值│中位数││符号│任何│任何│联合│联合...│├ ──────┼────────┼

......