深入浅出数据分析



以此写下读第一遍的感想,大体内容就是给一些数据然后进行一些思维上面的讲解,从中收获就是没有最优解,只有最合适的解决方法。读下来的感觉还是很不错的。
其中比较专业的技术也讲的很通俗易懂,大体内容就是样本拆分,深入数据,如果这数据给不了什么结果则继续细化。区别两个很难判断的东西的时候添加一些时间特性越近越精确,找出共性。有机会接触这方面的任务会继续深入学习和考虑他们的思考逻辑方法。

cut和count



uniq -c -f1是显示的某一个重复的地方才会计数,也就是说aab才是2,1。如果是aba的话就是111了。
因此一般来说都是cut之后进行排序然后再使用uniq的命令对其进行统计来显示。
当时team leader让我统计每个国家的人数。因此我使用了如下的shell脚本来计算最终的结果。
1
cut -d '"' -f 10 08-03-2015_09_36_28_AM_TRM_WW_NFS_Gamescom_Trailer_AUG15_20150803.txt|sort -k1,1|uniq -c

uniq的使用参数的含义如下

– c 显示输出中,在每行行首加上本行在文件中出现的次数。它可取代- u和- d选项。

– d 只显示重复行。

– u 只显示文件中不重复的各行。

– n 前n个字段与每个字段前的空白一起被忽略。一个字段是一个非空格、非制表符的字符串,彼此由制表符和空格隔开(字段从0开始编号)。

+n 前n个字符被忽略,之前的字符被跳过(字符从0开始编号)。

– f n 与- n相同,这里n是字段数。

– s n 与+n相同,这里n是字符数。

shell



网上看到一个需求:
需求如下:
有个TXT文本,里面记录如下:
“2015-07-19”,”AB”,”Y”
“2015-07-19”,”CD”,”N”
“2015-07-18”,”EF”,”Y”
“2015-07-19”,”GH”,”Y”
“2015-07-19”,”IJ”,”N”
每一行字符数都很规范
当第一列日期为当天,第三列为Y时,输出空文件,文件名为第二列_RUN,如AB_RUN,上面文本最终在相同目录下生成 AB_RUN,GH_RUN共2个空文件
当天日期有现成变量 $DATE 格式为字符型 如2015-07-19

忽然就想写一下来练习自己的awk和sed等语句。

1
file=a
sed "s/\"//g" $file |awk -F "," '{if($3=="Y") print($2"_RUN")}'>aa
filename=`awk '{print $1}' aa`
for f in $filename
do
  >$f
done
rm aa

当然还有好多种方法,我这个估计是比较简陋的一种了.

sort



linux中有一个sort命令是对数值来进行排序的。
sort [-bcfMnrtk][源文件][-o 输出文件]
-b 忽略每行前面开始出的空格字符。
-c 检查文件是否已经按照顺序排序。
-f 排序时,忽略大小写字母。
-M 将前面3个字母依照月份的缩写进行排序。
-n 依照数值的大小排序。
-o<输出文件> 将排序后的结果存入指定的文件。
-r 以相反的顺序来排序。
-t<分隔字符> 指定排序时所用的栏位分隔字符。
-k 选择以哪个区间进行排序。
sort一般是用升序来实现的,如果想要用降序来显示的话我们就得使用
1
sort -r xxx

而-u是用来去掉重复的数据
1
sort -u xxx

-n是以数值来进行排序的。

awk和sed常用命令



关于对在linux系统下文件的处理需要用到的就是功能比较强大的awk和sed这样的命令行来对数据进行处理。根据我使用的心得,我觉得sed或者grep或者gsed是用来搜索文件中你想要的数据并通过一系列的’#s can replace with a/ c/ d/ i/ p/ s/‘等关键字对该行进行显示,他不会改变原文的内容,如果想保存到其他文件中需要使用>来保存到新的文件当中。
网上有很多使用方法,我这记录自己常用的一些命令。sed的命令经常使用的是
1
sed 's/a/b/g' filename

g代表全局,将文中所有的a都替换成b。如果是c的话就会让你一个一个的确认是否需要替换。
1
2
#if without End, the script will run every time, otherwise only once
awk '/120545084047/ {count++;} END {print count "123"}' 06-29-2015_12_20_02_AM_TRM_WW_P4F_CCA_Reactivation_14d_TM_20150629.txt

-F represent separator

-f represent file

cut的使用格式
cut [-bn] [file] 或 cut [-c] [file] 或 cut [-df] [file]
-b字节
-c字符
-f区域

1
ifconfig | cut -f -2

代表1到2行
1
cut -d ' ' -f 2 跟 awk -F " " '{print $2}'

这两句意思是一样一样的。cut比较擅长一个字符间隔的文本内容
$#在linux代表的是参数的意思 -ne 代表的是不等于 -eq代表的是是否等于,只能跟数字和字符串相比。

union和unionall的使用



关于union的使用
Union,对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;
Union All,对两个结果集进行并集操作,包括重复行,不进行排序;
这个是网上大多的结论,我自己用mysql尝试了一下。
首先是原料的两句sql
1
mysql> select * from ja2;

结果如下:
+——+———+———-+
| id | name | score |
+——+———+———-+
| 4 | 4 | 4 |
| 5 | 5 | 5 |
| 6 | 6 | 6 |
| 10 | 4 | 2 |
+——+———+———-+
4 rows in set (0.00 sec)
1
mysql> select * from stu;

结果如下:
+——+———+———-+
| id | name | score |
+——+———+———-+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
+——+———+———-+
3 rows in set (0.01 sec)
1
2
3
mysql> select * from stu
-> union
-> select * from ja2

结果很显而易见:
+——+———+———-+
| id | name | score |
+——+———+———-+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
| 4 | 4 | 4 |
| 5 | 5 | 5 |
| 6 | 6 | 6 |
| 10 | 4 | 2 |
+——+———+———-+
7 rows in set (0.00 sec)

接下去我对这个对等的结果很好奇,是不是name对应的score,那么返回的是什么

1
mysql> select name from stu union all select score from ja2;

+———+
| name |
+———+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 2 |
+———+
这个是最后的结果,因为union会去重,最后的2会看不见,所以我就使用来unionall。虽然最终的结果是name,但是最后一个2的确是score上面的内容。

关于同一字段的加减处理的思路



昨天做到两个表的同一字段的处理就歇菜了,网上的资料也是说自身join自身,然后通过id来定位来进行相邻字段的相减。
第二个方法是通过case when来判断是什么类别的字段然后将其它表中的东西放入到这个字段中来。然后进行统一的处理。

1
2
3
4
case when Mailing_Group_Dev_Cnt <>0 then 1.000*Click_Cnt/Mailing_Group_Dev_Cnt else null end as CTR
,sum(case when CAMP_PRO.Lead_Type_Id = '60' then CAMP_PRO.Targeted_User_Cnt else 0 end) as Control_Group_Target_Cnt
,sum(case when CAMP_PRO.Lead_Type_Id = '999' then CAMP_REV.Email_Campaign_Cnvrsn_Cnt else 0 end) as Mailing_Group_Conv
,sum(case when CAMP_PRO.Lead_Type_Id = '60' then CAMP_REV.Email_Campaign_Cnvrsn_Cnt else 0 end) as Control_Group_Conv

笛卡尔



关于sql的笛卡尔联结,无论是否增加where,其中进行的依然是笛卡尔连接,但是不加限制条件会有大量的数据。
1
select * from a,b

1
select * from a,b where a.id=b.id

以上都会进行全表扫描。

groupby在teradata的个人笔记



在teradata中Selected non-aggregate values must be part of the associated group.这个错误一直出现,虽然知道是groupby的问题,但是根本原因还是不太清楚,这次用这来祭奠我的groupby。
根据网上的教程说明的是groupby需要配合聚合函数来使用分组,起初我以为就是groupby的话就必须select出这个groupby的函数,后来的例子让我发现我错的还是比较离谱的。
1
select sum(process.Target),sum(conversion.cnvrsn) from (select sum(Targeted_User_Cnt) as Target,offer_id from EADW_ADM_APP.CAMPAIGN_PROCESS  where Campaign_Id='1000dq9pd69b' and lead_type_id='999' group by offer_id) process left join (select sum(email_campaign_cnvrsn_cnt) as cnvrsn,offer_id from EADW_ADM_APP.CAMPAIGN_CONVERSION  where Campaign_Id='1000dq9pd69b' and lead_type_id='999' group by offer_id) conversion on conversion.offer_id=process.offer_id;

上面这个sql语句是可以得出想要的结果,从中看出select中不需要添加任何offerid就能获得我想要的结果。
1
350047,368

以上的数据就是最终产生的结果,与我单个sql语句生产的结果是一样的。
1
Select sum(Targeted_User_Cnt) from EADW_ADM_APP.CAMPAIGN_PROCESS where Campaign_Id='1000dq9pd69b' and lead_type_id='999';

通过跟上司的讨论,因为两个表细致程度不是一样的,所以很容易就产生一对多的问题,这样重复数据对结果影响还是比较大的,所以我就提高维度,把我不需要关心的数据直接去除掉,不降低维度是因为比较麻烦,而去意义并不大。

groupby根据2个字段中数据范围比较大的来对此进行分组。可以将两个细致程度不一样的表进行数据的统计,可以防止一对多,然后对总的数据进行groupby,这样就可以获得自己想要的数据。

1
2
3
4
5
6
7
8
9
10
11
12
select Pr.Lead_Type_Id, sum(Email_Delivered_Cnt), sum(Targeted_User_Cnt),
sum(case when Co.email_conv is null then 0 else Co.email_conv end) as total_conv
from EADW_ADM_APP.CAMPAIGN_Process Pr left join
(select CC.Campaign_Collection_Id as Col_Id, CC.Campaign_Id as Cam_Id, CC.Generation_Date as G_date, CC.Segment_Id as Seg_Id,
CC.Lead_Type_Id as Lead_Id, CC.Country_Code as Cou_Code,
CC.Offer_Id as O_Id, CC.Step_Id as S_Id, CC.Step_Order_Nbr as S_Nbr, sum(email_campaign_cnvrsn_cnt) as email_conv from EADW_ADM_APP.CAMPAIGN_Conversion CC group by 1,2,3,4,5,6,7,8,9
) Co
on (Pr.Campaign_Collection_Id=Co.Col_Id and Pr.Campaign_Id=Co.Cam_Id and
Pr.Generation_Date=Co.G_Date and Pr.Segment_Id=Co.Seg_Id and
Pr.Lead_Type_Id=Co.Lead_Id and Pr.Country_Code=Co.Cou_Code and
Pr.Offer_Id=Co.O_Id and Pr.Step_Id=Co.S_Id and Pr.Step_Order_Nbr=Co.S_Nbr)
where Pr.Campaign_Id='1000dq9pd69b' group by 1;

以上是为上司的sql语句,对于我的sql语句rightjoin和leftjoin没啥区别,过去笼统,需要更多数据的话则需要改动。groupby后跟数字的话代表的是select后面的字段顺序。

在groupby的having语句中,是闲where出来之后对group后的结果在进行having过滤。在groupby中可以直接这样使用

1
select a,b from table group by 1,2

但是不能直接在having后面这样用数字来替换ab,会报string类型和number类型的错误

join的一些使用



join和一些其他关键字联合起来使用的时候给我不了解的地方就是sql的执行顺序。特意去图书馆找了本sql进阶书籍观看,以此记下心得。join的其他用法网上资料有一堆,并不继续讲述。

关于以下语句成功执行出我想要的结果,left join的时候最好把数据较多的表放在左表当中,我用right join的时候给我的数据并不完整,其中缘由我需要整理一下。

sql的执行顺序为from后面的表与join先执行,那么就是两表直接连接,接着通过on来限制对接的数据列,然后where对结果进行筛选,然后进行groupby来分组之后进行选出。

1
$ select case when conversion.offer_Id='1000dm0bj6gq' then 'PS4' else 'PS3' end as Collateral,sum(conversion.email_campaign_cnvrsn_cnt),sum(process.Targeted_User_Cnt) from EADW_ADM_APP.CAMPAIGN_PROCESS process left join EADW_ADM_APP.CAMPAIGN_CONVERSION conversion on conversion.Campaign_Id=process.Campaign_Id and conversion.offer_Id=process.offer_Id and conversion.Segment_Id=process.Segment_Id and conversion.Step_Order_Nbr=process.Step_Order_Nbr and conversion.Step_Id=process.Step_Id and conversion.Country_Code=process.Country_Code and conversion.Campaign_Collection_Id=process.Campaign_Collection_Id and conversion.Generation_Date=process.Generation_Date and conversion.Lead_Type_Id=process.Lead_Type_Id  where process.Campaign_Id='1000dm0bj63b' and process.lead_type_id='999' group by process.offer_Id;

以前写的sql,on跟的限制条件不多,所以对接的时候有一堆重复数据,如果没有约束的时候,仅用普通的列相对接的话,要考虑好限制条件。