Loading...
墨滴

yuanzhoulvpi

2021/06/05  阅读:26  主题:默认主题

数据的长和宽格式

数据的长表和宽表的使用与注意事项

背景

在工作的时候遇到这样的问题:有六个部门,老板想看各个部门的、本月的每天的各个指标(gmv、nmv、毛利额、下单用户数、订单数)。最后将数据导出保存为excel格式的供他们使用。

关于使用R语言的ggplot2的时候,虽然我的表是宽表,但是我还是想将这个表的每一列都画在ggplot2上。很明显,我可以一列一列的画,但是这么做实在是太低效了,我怎么做才是最高效的。

介绍

基于上面的这两个背景,我们来简单介绍一下,什么叫“长数据”、“宽数据”。

“长数据”:长数据的格式一般分为三列。第一列叫index,也叫索引。第二列叫variable_name,也叫变量类型。第三列叫value,也叫值。

“宽数据”:宽数据的格式一般分为两列,第一列叫index,也叫索引,第二列叫variable,叫做维度的名字。这里和“长数据”比较起来就是少了一个“value”。

就拿R语言里面最常见的一个鸢尾花数据集,iris:

library(tidyverse)
iris %>% sample_n(4)
#  Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
# 1          5.0         3.4          1.6         0.4     setosa
# 2          5.6         2.9          3.6         1.3 versicolor
# 3          6.4         2.9          4.3         1.3 versicolor
# 4          5.1         3.5          1.4         0.3     setosa

上面的这个数据就是一个最简单的、最标准的“宽数据”。首先Species这一列代表一个index。这个索引对应的就是一个物种类别;像Sepal.Length Sepal.Width Petal.Length Petal.Width这4列就是代表每一个物种的维度。这个维度也就是上面我们定义的variable,也就是变量。从每一行(横着看)来看,我们可以看到每一个样本的4个维度。也就是说我们很明显知道第一个样本的Sepal.Length是5.0;Sepal.Width是3.4;…… 以此类推。这就是“宽数据”给人的感觉:一行数据可以看完一个样本的所有维度。但是长数据就不行。

长数据给人的感觉是什么样子?,长数据是这样的:

iris %>% pivot_longer(cols =  - Species)
#    Species name         value
#    <fct>   <chr>        <dbl>
#  1 setosa  Sepal.Length   5.1
#  2 setosa  Sepal.Width    3.5
#  3 setosa  Petal.Length   1.4
#  4 setosa  Petal.Width    0.2
#  5 setosa  Sepal.Length   4.9
#  6 setosa  Sepal.Width    3  
#  7 setosa  Petal.Length   1.4
#  8 setosa  Petal.Width    0.2
#  9 setosa  Sepal.Length   4.7
# 10 setosa  Sepal.Width    3.2
# … with 590 more rows

有没有看出来,这样每一行只能看到一个index,一个维度,一个value。这个和我们之前能一行可以看4个维度有了天壤之别;而且长数据也变成了600行,但是之前的”宽数据“也才150行。

总结:

最后一句人话就是,“宽数据”可以一行看到这个样本的所有维度和类别,“长数据”一行只能看到一个维度和这个维度对应的值。

对一个数据来说,这个数据的长与宽是相对的。不能说这个一定是“长数据”或者一定是“宽数据”,只能说这个表相对于另外一个表来说是更宽一点或者更加长一点。另外数据的长与宽在转换的过程中,数据的信息量是保持不变的。

一个简单的案例

我有6个部门,这6个部门的2021年5月1日的gmv、nmv、毛利额、下单用户数如下:

example_data1 <- tibble('part_name' = paste0('part_'1:6),
       'gmv' = runif(n=6,min = 20, max=30),
       'nmv' = gmv - runif(n = 6, min = 0, max = 2),
       'num_user' = runif(n = 6, min = 10, max = 20))

example_data1

# # A tibble: 6 x 4
#   part_name   gmv   nmv num_user
#   <chr>     <dbl> <dbl>    <dbl>
# 1 part_1     24.7  23.4     15.1
# 2 part_2     27.6  27.5     10.1
# 3 part_3     24.4  23.4     14.6
# 4 part_4     26.4  25.9     12.7
# 5 part_5     27.5  25.6     10.2
# 6 part_6     24.0  23.8     14.1

上面的数据第一列是部门名字,第二列是gmv(总销售额),第三列是nmv(净销售额),第四列是num_user(下单用户数)。 现在这个表从每一行的角度来看,可以看到一个部门的所有指标,那么我姑且叫这个表为“宽数据”。 下面这个表是长数据:可以看到这表每一行只能看到一个部门的一个指标对应的值。那么我们就能将这个表称为”长数据“

example_data1 %>% pivot_longer(cols = -part_name)

# # A tibble: 18 x 3
#    part_name name     value
#    <chr>     <chr>    <dbl>
#  1 part_1    gmv       24.7
#  2 part_1    nmv       23.4
#  3 part_1    num_user  15.1
#  4 part_2    gmv       27.6
#  5 part_2    nmv       27.5
#  6 part_2    num_user  10.1
#  7 part_3    gmv       24.4
#  8 part_3    nmv       23.4
#  9 part_3    num_user  14.6
# 10 part_4    gmv       26.4
# 11 part_4    nmv       25.9
# 12 part_4    num_user  12.7
# 13 part_5    gmv       27.5
# 14 part_5    nmv       25.6
# 15 part_5    num_user  10.2
# 16 part_6    gmv       24.0
# 17 part_6    nmv       23.8
# 18 part_6    num_user  14.1

解决问题

用这个部门数据来回答我上面那个ggplot2的画图问题:我现在想画这六个部门的折线图,三个指标都要用上,我应该怎么做? 小白是这么做的:

example_data1 %>% ggplot() +
  geom_line((aes(x = part_name, y = gmv, group=1))) +
  geom_line((aes(x = part_name, y = nmv, group=1))) +
  geom_line((aes(x = part_name, y = num_user, group=1))) 

图我就不贴了,真的是要多丑有多丑。

最好的方法是这么做:

example_data1 %>% pivot_longer(cols = -part_name) %>% 
  ggplot(aes(x = part_name, y = value, color=name)) +
  geom_line(aes(group = name))

复杂一点的问题

上面六个部门也只是在5月1日的数据,如果我有十天的数据呢。怎么办? 先把数据给创造出来:这个样式是我们从sql中导出的数据样式

import numpy as np 
import pandas as pd 
import datetime
from itertools import product


days_51 = [datetime.datetime(year=2021, month=5, day=i).date() for i in range(16)]
part_name = [f"part_{i}" for i in range(17)]


example_2 = pd.DataFrame(list(product(part_name, days_51)))
example_2.columns = ['part_name''date']
example_2['gmv'] = np.random.randint(low=20, high=40, size=30)
example_2['nmv'] = example_2['gmv'] - np.random.randint(low=2, high=4, size=30)
example_2['num_user'] = np.random.randint(low=40, high=400, size=30)
example_2


#    part_name        date  gmv  nmv  num_user
# 0     part_1  2021-05-01   38   36        53
# 1     part_1  2021-05-02   35   33       363
# 2     part_1  2021-05-03   38   35       325
# 3     part_1  2021-05-04   21   19       346
# 4     part_1  2021-05-05   27   25       111
# 5     part_2  2021-05-01   29   27       251
# 6     part_2  2021-05-02   21   18       301
# 7     part_2  2021-05-03   22   20        68
# 8     part_2  2021-05-04   38   35       140
# 9     part_2  2021-05-05   28   26       365
# 10    part_3  2021-05-01   26   23       164
# 11    part_3  2021-05-02   32   29        54
# 12    part_3  2021-05-03   20   18       189
# 13    part_3  2021-05-04   24   22       291
# 14    part_3  2021-05-05   24   22       261
# 15    part_4  2021-05-01   37   34       278
# 16    part_4  2021-05-02   32   30       240
# 17    part_4  2021-05-03   30   28       335

接下来,我们将上面这个数据导出为领导愿意看的样式,也就是下面这个样式:

是不是很amazing!!!😊 其实代码很简单:

example_2.melt(id_vars=['part_name''date']).pivot_table(index=['part_name'], columns=['date''variable']).to_excel("final_data.xlsx")

总结

上面就是我遇到的一些小问题,这次是把“长宽数据”做个总结,并且结合实际情况,做一些使用。数据全部都是随机生成。希望就是分享给大家思想,更多的函数介绍,大家多多百度。

yuanzhoulvpi

2021/06/05  阅读:26  主题:默认主题

作者介绍

yuanzhoulvpi