Linux
常见错误
- var=? 等号两侧不能空格
- 管道命令生成子shell,无法修改全局变量的值
cat file | while read line
do
var=var$line
done # 实际var还是原来的值
- 待定
常见指令
- $的N种用法
命令 | 作用 |
---|---|
${x} | 引用变量 |
${#x} | 变量x的长度 |
$n | 传递参数,0是文件名,n是第n个参数 |
$# | 传递到脚本的参数个数 |
$* | ”$*“ 等价于 “$1 $2 … $n” |
$@ | “$@” 等价于 “$1” “ 2 " … " 2" … " 2"…"n” |
${#1} | 第一个参数的长度 |
$? | 返回上一次的执行结果,0表示成功 |
$() | 命令替换 |
$[] | 表达式求值,$[]用于插值,而expr则将值输出 |
- 读取文件内容
while read line
do
done <file
# 分隔符可能会导致只取出一个值
IFS=','
for line in $(cat file)
do
echo $line
done
awk
- 数学表达式
let i=$i+1
- 待定
任务实例
- 统计一周内的分布情况
# 定义列表
STAT_DAYS=(${YYYYMMDD} ${YYYYMMDD_1DaysAgo} ${YYYYMMDD_2DaysAgo} ${YYYYMMDD_3DaysAgo} ${YYYYMMDD_4DaysAgo} ${YYYYMMDD_5DaysAgo} ${YYYYMMDD_6DaysAgo})
# data存放一周7天的数据
mkdir data
for date in ${STAT_DAYS[*]}
do
# 判断当天的统计数据是否已经生成
hadoop fs -test -e hdfs://search-offline/user/stat/xxx/${date}
if [ $? -eq 0 ] ;then
echo 'exist'
else
python check_pr_dist.py --day ${date}
hdfs dfs -get hdfs://search-offline/user/stat/xxx/${date}/*.csv data #将hdfs文件拉取到本地
fi
done
- 以html语法发送邮件
v_str=''
IFS=',' #定义分隔符
while read line
do
v_str=$v_str'<tr>'
for val in $line
do
v_str=$v_str'<td>'$val'</td>'
done
v_str=$v_str'</tr>'
done <data/data.csv
echo $v_str
content="
<head>
<style>
table{
text-align: center;
}
</style>
</head>
<body>
<br>
<table border='1' cellpadding='12' cellspacing='0'>
<th colspan='7' bgcolor='yellow'>${YYYYMMDD_6DaysAgo}-${YYYYMMDD} 沉浸式曝光统计</th>
<tr bgcolor='yellow'><td>duration</td><td>prop</td><td>count</td><td>pr<=0.3</td><td>(0.3,0.6]</td><td>pr(0.6,0.9]</td><td>pr>0.9</td></tr>
${v_str}
</table>
<br>
<div>duration指区间(0,5]等,prop记录每个duration区间占整体的比例,pr统计该区间内的分布</div>
</body>
"
echo $(date +%Y-%m-%d:%T) "${content}"
echo ${content} > result.txt
Hadoop
常用指令
文件路径增删改查
command | 作用 |
---|---|
hdfs dfs -mkdir dir | 创建文件夹 |
hdfs dfs -rm -r dir | 删除文件夹 |
hls=hdfs dfs -ls | 查看目录文件信息 |
hdfs dfs -stat path | 返回指定路径信息 |
文件操作
command | 作用 |
---|---|
hcat=hdfs dfs -cat file | 查看文件 |
hdfs dfs -rm file | 删除文件 |
hdfs dfs -put file dir | 上传文件file至dir路径 |
hdfs dfs -get file dir | 下载file文件至本地dir |
hdfs dfs -mv filea fileb | 将a文件移动至b文件 |
判断系列
command | 作用 |
---|---|
hdfs fs -test -e file | 判断file是否存在,正0负1 |
hdfs fs -test -d dir | 判断dir是否为目录 |
hdfs fs -test -z file | 判断文件是否为空 |
pyspark
常见错误
- 需要保留的字段必须出现在groupby([])中,否则最后的DF不显示该字段,且不能出现在agg函数中
- DF不能使用map(lambda x:func(x)),需要rdd.map,注意rdd不能用write…csv
- rdd.flatMap函数需要先转成dict row = row.asDict()
- spark读入textfile不能把每一行的处理函数(_extract_info)放在类内,且最后返回的是一个list,否则不能转化成DF
读存数据
## 读入文件
#### 处理后的数据
schema = StructType([
StructField('user_id', StringType()),
...
])
spark.read.option('delimiter','\t').csv(path, header=False, schema=schema).cache()
#### 常用于读取原始日志json格式
def extract_info(row):
try:
row = json.loads(row)
event_obj = row.get("event_info", "")
if event_obj == "":
return None
scene_id = event_obj.get("pageID", "")
user_id = row.get("buuid", "")
doc_id = row.get("docId", "")
if user_id == "" or doc_id == "" or len(user_id) not in [17, 18] or len(doc_id) != 18:
return None
return [user_id, doc_id]
except:
return ""
spark.sparkContext \
.textFile(path) \
.map(lambda x: extract_info(x)) \
.filter(lambda r: r is not None and r != '') \
.toDF(['col1', 'col2',...]) \
## 存入文件
df.write.option("header", "false").mode("overwrite").csv(path, sep='\t')
rdd.saveAsTextFile(path)
常见指令
from pyspark.sql.functions import udf
# 切片
df.select('col')
df.col
df[['col']]
# 删除
df.drop('col')
# 增加
df.withColumn('col', func)
# 分组统计
df.groupby('col').agg()
# 别名
df.col.alias('col')
# 筛选数据
df[df.col==?]
df.filter(condition)
df.where(condition)
# 计数
df.count()
# 自定义函数
udffunc = udf(func, StringType())
df.withColumn('col', udffunc(df.col))
## 数据关联
df.join(df2, condition, how='inner')
## 排序
df.orderBy(col, F.desc(col))
pyspark.sql.functions(F)
函数 | 作用 |
---|---|
F.collect_list(‘col’) | 列转行(元素可重复) |
F.collect_set(‘col’) | 列转行 |
F.explode(’col‘) | 行转列 |
F.concat(*cols) | 拼接输入列 |
F.lit(val) | 生成值为val的列 |
F.split(col) | 将该列转成一个list |
F.size() | |
F.col() |
rdd
df.toDF # rdd转DF
df.take() # 等价于df.head()
# 对每行的作用
df.rdd.map(lambda x:function(x))
df.rdd.flatMap(lambda x: function(x))
# hash法
zipWithIndex() # 注意column要去重
任务实例
- 将col1和col2两个字段合并且转成list
输入:col1和col2都是string,例如’num1,num2,num3’
df.withColumn('col', F.split(F.concat('col1',F.lit(','),'col2'),','))
- 根据id_list生成对应的title_list
输入:df1[‘id_list’,…] df2[‘id’,‘title’]
其中’id_list’=[‘num1’,‘num2’,…]
df1.select(..., F.explode('id_list').alias('id')) \
.join(df2, on='id', how='inner') \
.agg(F.collect_list('title'))
行转列,列转行有可能内存占用太大,导致失败
df2 = df2.rdd.map(lambda x:(x.asDict()['id'], x.asDict()['title']))
df2 = spark.sparkContext.broadcast(df2.collectAsMap())
def id2title(id_list,id_title_map):
title_map = []
for id in id_list:
if id_title_map.has_key(id):
title_map.append(id_title_map[id])
return title_map
udf_id2title = F.udf(lambda x:id2title(x,df2.value), ArrayType(StringType()))
df.withColumn('title_list',udf_id2title('id_list'))
- 为每个item生成它之前的item_list
输入:user_id + item_list
def gen_pair(row):
row = row.asDict()
...
result = []
for i, item_id in enumerate(item_list2):
# 只保留最近50个的click
if i != 0:
item_pre = ','.join(item_list2[max(i-50, 0):i][::-1]) # 将最近的排在最前面
result.append([user_id, item_id, item_pre])
return result
df.rdd.flatMap(lamda row: gen_pair(row)).toDF(['user_id', 'item_id', 'item_list'])
- 抽取每个user最新的click
def user_topn(df, part_key, desc_sort_by, topn):
window = Window.partitionBy(part_key).orderBy(F.desc(desc_sort_by))
rank_name = '{}_rank'.format(desc_sort_by)
# row_number得到连续而不重复的排名
grouped_and_ranked = df.withColumn(rank_name, F.row_number().over(window))
result = grouped_and_ranked.dropDuplicates(subset=[part_key, rank_name])
if topn != '':
result = result.filter(F.col(rank_name) <= topn)
return result
user_topn(df, key, col, 1)
- 待定