同学去哪了

前些天发现就业网公布了14届毕业生去向统计表,刚好想学gnuplot,心血来潮要把统计表做成图片。

xls转csv

首先要做的是将xls格式转换成便于脚本处理的csv格式。一开始使用excel另存。后来发现Linux上有可用的工具(可惜没成功,貌似是编码的问题,这里仅记录一下软件安装)。添加epel源后可用直接安装。

epel详见http://mirrors.fedoraproject.org/publiclist/EPEL/。选择一个国内的安装:

rpm -ivh http://mirrors.yun-idc.com/epel/6/x86_64/epel-release-6-8.noarch.rpm

yum search查询xls2csv

[root@HADOOP-219 hnu-graduates]# yum search xls2csv
Loaded plugins: fastestmirror, priorities
Loading mirror speeds from cached hostfile
 * epel: ftp.sjtu.edu.cn
======================================== Matched: xls2csv =========================================
catdoc.x86_64 : A program which converts Microsoft office files to plain text

之后yum安装catdoc即可。

另外有些文章和软件可以参考一下:

  • http://leslie-chu.blog.163.com/blog/static/199863243201263935085/
  • xls2doc

处理csv文件

根据关键词获取具体去向。关键词从命令行参数获取。支持以学号为关键词,但是为了好理解会附加一个变量即ADD_KEY。当KEY为数字的时候,取专业那一列作为ADD_KEY。

if [ $# -eq 2 ];then
	if [ $2 -lt 500 ];then
		SHOW_NUM=$2
		KEY=""
	else
		 KEY=$2
		SHOW_NUM=0
	fi
else
	KEY=$2
	SHOW_NUM=$3
fi
FILE="hnu2014.csv"
REPLACE="replace.dat"

OUT=$KEY.csv
[ "$KEY"x == ""x ] && OUT=all.csv

COMPANY=$KEY.txt
[ "$KEY"x == ""x ] && COMPANY=all.txt

PIC=$KEY
[ "$KEY"x == ""x ] && PIC=all


echo >$COMPANY
grep "$KEY" $FILE >$OUT

echo $KEY |grep -E "[0-9]" && ADD_KEY=`grep $KEY $FILE |awk -F "," '{print $4}' |sort |uniq -c |sort -n -r | awk 'NR==1{print $2}'`

TOTAL=`wc -l "$OUT" |awk '{print $1}'`
TITLE="湖南大学 $KEY $ADD_KEY 2014届 $TOTAL 名毕业生去向(取前 $SHOW_NUM 强)"
FONTS="/usr/share/fonts/wqy-microhei/wqy-microhei.ttc"

去向不区分派遣升学和回原籍灵活就业,所以 报到证开具单位、到人才备注单位和回原籍备注单位统一处理,具体规则是,默认以报到证开具单位为去向,如果到人才备注单位或者回原籍备注单位不为空,则去向用它们的值覆盖。

function func()
{
	company=`echo $id | awk -F "," '{print $7}'`
	r_company=`echo $id | awk -F "," '{print $8}'`
		[ "$r_company"x != ""x ] && company=$r_company
	p_company=`echo $id | awk -F "," '{print $9}'`
		[ "$p_company"x != ""x ] && company=$p_company
	echo $company >>$COMPANY
	sleep 3
}

为了加快处理速度,模拟一个多线程。

tmp_fifofile=/tmp/$ $.fifo  #$ $之间本无空格,高亮插件bugs不支持无空格形式
mkfifo $tmp_fifofile
exec 6<>$tmp_fifofile
rm $tmp_fifofile

for [1]i=0;i<$thread;i++;do
	echo 
done >&6

while read id;do
	read -u6
	{
		func
		echo >&6
	} &
done < $OUT


wait
exec 6>&-

对具体去向进行处理, 主要是统一去向名称和去重计数及排序。

file=$COMPANY
#sed -i "/^$/d" $REPLACE
while read id 
do
	echo $id |grep -E "#|^$" && continue
	search=`echo $id |awk '{print $1}'`
	replace=`echo $id |awk '{print $2}'`
	if [ "$replace"x == ""x ];then
		sed -i "s/$search//g" $file
	else
		sed -i "s/.*$search.*/$replace/g" $file
	fi
done<$REPLACE

sed -i '/^$/d' $file
sed -i 's/\ //g' $file
sed -i "s/\(.*银行\).*行.*/\1/g" $file
sed -i "s/\(.*公司\).*公司/\1/g" $file
sed -i "s/\(.*公司\).*研究所/\1/g" $file
sed -i "s/\(.*银行\).*/\1/g" $file
sed -i "s/\(.*集团\).*/\1/g" $file
sed -i "s/\(.*局\).*/\1/g" $file
sed -i "s/\(.*大学\).*学院.*/\1/g" $file
sed -i "s/中国\(香港.*\).*/\1/g" $file
sed -i "s/.*香港\(香港.*\).*/\1/g" $file
sed -i "s/.*美国\(美国.*\).*/\1/g" $file
sed -i "s/.*英国\(英国.*\).*/\1/g" $file
sed -i "s/.*日本\(日本.*\).*/\1/g" $file
sed -i "s/.*澳大利亚\(澳大利亚.*\).*/\1/g" $file

cat $file |sort |uniq -c |sort -n -r >all.tmp

统一名称主要基于一个替换规则文件$REPLACE,文件的格式如下。第二列为空的表示删除第一列。

#研究生相关
保送
考取
博士生
研究生
中国科学院 中国科学院
中科院 中国科学院
中国科技大学 中国科学技术大学
中国科学技术大学 中国科学技术大学


#部队及国家机关
军区 部队
信部
气象局 气象局
海事局 海事局


#信科院
联合网络通信 中国联通

用gnuplot画图

想用饼图,搜了之后得知gnuplot不支持饼图,只好用柱状图了。

gnuplot<<EOF
set term png size $WIDTH,$HIGHT font "$FONTS,$FONTSIZE px"
set output "./img/$PIC-h.png"
set style data histograms
set grid
set title "$TITLE" offset graph -$OFFSET
set style fill solid 2.00 border
set xtic rotate by 300
set ytics $MOST
plot "tmp" using 1:xtic(2)
EOF

其中set style data histograms就是指定画柱状图。

在这里折腾时间比较长的标题的偏移。一开始做的是水平的柱状图,图片很宽,不容易看到标题,需要把标题移动到左边。但是offset默认单位是character,不能明确的指定位置。经过长时间的搜索和调试,发现以graph为单位时比较容易控制。

gnuplot> help coordinate
 The commands `set arrow`, `set key`, `set label` and `set object` allow you
 to draw something at an arbitrary position on the graph.  This position is
 specified by the syntax:

       {<system>} <x>, {<system>} <y> {,{<system>} <z>}

 Each <system> can either be `first`, `second`, `graph`, `screen`, or
 `character`.

 `first` places the x, y, or z coordinate in the system defined by the left
 and bottom axes; `second` places it in the system defined by the second axes
 (top and right); `graph` specifies the area within the axes---0,0 is bottom
 left and 1,1 is top right (for splot, 0,0,0 is bottom left of plotting area;
 use negative z to get to the base---see `set ticslevel`); `screen`
 specifies the screen area (the entire area---not just the portion selected by
 `set size`), with 0,0 at bottom left and 1,1 at top right; and `character`
 gives the position in character widths and heights from the bottom left of
 the screen area (screen 0,0), `character` coordinates depend on the chosen
 font size.

graph已图片左下角为 0,0,右上角为 1,1,根据图片宽度及字体大小(12pt的字体大概宽16px),测算偏移量。一般越宽的图片偏移量越大。下面代码中WIDTH是图片宽度。

OFFSET=`echo $WIDTH |awk '{print 0.5-16*10/$WIDTH}'`

 Convert转换图片

为了便于在网上发布,想要做成垂直的柱状图,gnuplot没有找到方法,就用convert旋转图片(需yum安装ImageMagick)。

gnuplot<<EOF
set term png size $WIDTH,$HIGHT_V font "$FONTS,$FONTSIZE px"
set output "./img/$PIC-v.png"
set boxwidth 2 absolute
set style data histograms
set grid
#set title "$TITLE" offset graph -$OFFSET
set style fill solid $MOST border -1
set xtic rotate by 90
set xtic offset 1,0
set ytic rotate by 90
set ytics $MOST
plot "tmp" using 1:xtic(2)
EOF

这时折腾很久的gnuplot的offset就难排上用场了,调来调去总是不能满意。好在convert命令可以添加文字注释。convert的坐标大概是已像素为单位,做上角为0,0,往右往下增加。于是确定添加TITLE的位置为:

TITLE_OFFSET=160
[2]RESIZE_ORIGIN=$TITLE_OFFSET-20
[3]R_WIDTH=$HIGHT_V-$RESIZE_ORIGIN

然后坐标也需要旋转,横坐标为公司名称,逆时针旋转90度后和柱图有些错位,因此将横坐标右移一个1符的偏移量:

set xtic rotate by 90
set xtic offset 1,0

convert旋转并添加标题注释。

convert -rotate 90 img/$PIC-v.png img/$PIC-v.png.tmp
convert -font $FONTS -pointsize 16 -draw "text $TITLE_OFFSET,26 '$TITLE'" img/$PIC-v.png.tmp img/$PIC-v.png && rm -f img/$PIC-v.png.tmp

旋转后图片左边留白太多,剪切掉。

convert -crop "$R_WIDTH x$WIDTH+$RESIZE_ORIGIN+0" img/$PIC-v.png img/$PIC-v.png.tmp && mv img/$PIC-v.png.tmp img/$PIC-v.png

结果展示

垂直图

信科院14届

信科院14届

水平图

2010081-h

2010081-h

附:完整代码见 https://github.com/annProg/hnu-graduates

参考资料

参考资料
1 i=0;i<$thread;i++
2 RESIZE_ORIGIN=$TITLE_OFFSET-20
3 R_WIDTH=$HIGHT_V-$RESIZE_ORIGIN

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注