Python 分析在德的中国程序员,告别 996 ?

640?wx_fmt=gif

640?wx_fmt=jpeg

作者 | 李辉

责编 | 胡巍巍

出品 | 程序人生(ID:coder_life)

今年5月27日, 一位据说在德国的中国程序员@将记忆深埋在微博公布:

“半年时间,100多TB数据, 利用1024、91、sex8、PornHub、xvideos 等网站采集的数据对比Facebook、instagram、TikTok 、抖音、微博等社交媒体。我们在全球范围内成功识别了10多万从事不可描述行业的小姐姐。”

热炒之下,这套Deep Learning系统瞬间炸了锅,顺便炸翻了在德国处于懵逼状态的一众平时安安静静老老实实的程序员:我们身边竟然藏着这样一个人?!

这引起了我对在德中国籍程序员的行业分类以及专业方向等相关数据的好奇。长期以来德国一直面临着劳动力短缺,特别是工程技术方向,尤其是IT专业人才的极度缺乏,以至于德国政府将这些专业的人才获得欧盟蓝卡的最低年薪标准降到了税前41808欧元(2019)。换句话说,软件信息专业的同学毕业后在德国很容易找到工作,并且获得蓝卡工作居留许可。近年来身边来自印度,俄罗斯,中国的程序员也在逐年增加。那么中国程序员在德国到底从事那些行业呢?

 

640?wx_fmt=png

蓝卡和德国程序员数据

 

先在网上找了一圈,没有找到特别针对中国籍程序员的数据分析,只找到关于蓝卡和在德国工作的程序员的数据分析。

蓝卡数据

2013-2018年,超过76000外籍人员持蓝卡在德国工作。2017年德国共有21727外国人申请蓝卡工作签证,其中中国国籍申请者占了近10%。这说明仅2017年,就有二千多中国籍雇员申请了蓝卡,这其中IT从业者占比未知。假设IT软件信息领域的中国雇员只占比其中10%,那么过去五年中就有约800名中国籍程序员拿到蓝卡。实际上根据生活和工作的接触,我保守估计在德中国籍程序员数量超过1500人。

640?wx_fmt=jpeg

640?wx_fmt=jpeg

据2016年数据,欧盟蓝卡签证的所在申请国,84%位于德国,可以说几乎整个欧盟的外国工程师都来德国找工作了。

640?wx_fmt=jpeg

 

640?wx_fmt=png

在德国工作的程序员数据

 

据来自Stack Overflow的德国IT数据分析,2016年全德国有超过120000软件开发人员,2017年暴增超过820000。不过82万这个数字不可信,毕竟德国总人口才八千多万,如果是将近1%的占比,德国不至于一直闹码农荒。可信的十几万程序员中,软件开发方向数据如下:Web开发占比65.51%,系统管理员位居第二,数据库管理员第三。仅仅这三个方向就吃掉了75%的占比,为什么德国程序员看起来很偏科,爆火的机器学习和数据分析才各占4%左右。

这是因为德国IT行业大多为德国的支柱产业服务,如汽车、制药、机械、电子等,这些公司所需的企业内部管理软件如今多为SaaS构架,同时因为传统行业对云服务的怀疑和不信任态度,亦或安全原因,他们又维护着大量的企业私有服务器,和企业级数据库。所以不难理解前三甲总合占比之大。

虽说国内的移动开发趋势这两年有点弱,但德国的iOS和Android移动开发就从来没有强过,因为缺乏B2C土壤,传统企业一般也不重视移动开发(未必需要),相关产业很多都外包于东欧或者印度,中国的团队。

640?wx_fmt=png

在德中国程序员数据分析

网络上暂时没有发现任何关于这些可能存在的1500名中国程序员的数据,这就尴尬了,没数据怎么分析?

—-本文只好结束—- 

等等,平时管理的几个德国的IT行业微信群不就是最好的数据源?群友加起来也有500多人了,样本虽不大,但毕竟还是遵循正态分布的。不过必须用Python 3开发一套脚本来收集和处理相关数据。

 

640?wx_fmt=png

在德中国程序员做什么

 

专业方向&工作领域&开发语言和框架的数据采集

如果使用匿名调查报告方式,扰民且又费时费力,此类信息只能从群昵称上打主意了,首先是发群公告规范群友昵称标准:


 

大部分群友按标准改了昵称,但是还有一部分死硬派坚决不改,又不能经常发群消息提醒,只能开发机器人自动提醒了。微信机器人Wxpy是一个包装得非常简洁的微信个人号 API, 在 itchat 的基础上,通过大量接口优化提升了模块的易用性,并进行丰富的功能扩展,一些常见的场景:

  • 运行脚本时自动把日志发送到你的微信

  • 群成员信息获取,邀请或者踢人

  • 跨号或跨群转发消息

  • 监听群聊或者单聊的信息

  • 通过脚本和第三方API交互,比如图灵聊天机器人、智能办公、智能家居

开发需求

  • 获取群聊群成员信息

  • 找出所有群昵称不符合标准的群友

  • 随机抽取5人,在群里发布改昵称提醒消息

  • 同时将这次提醒的5人,存储进数据库

  • 每天早八点晚八点两次定时启动昵称检查脚本

  • 某人在将来被提醒次数超过10次,还不予配合不改昵称时,将自动踢出群

  • 新群友被邀请进入群时,立刻发送群规提示改昵称

开发分解

该任务所需第三方库如下:


 

pip3 install wxpy
pip3 install apscheduler
pip3 install pymysql
pip3 install DBUtils

1. 建库建表

本文采用的是MySQL,后期可以扩展支持Postgre或者MongoDB。

因为需要存储微信表情字符集,所以表的默认编码采用utf8mb4_unicode_ci。


 

DROP TABLE IF EXISTS `wx_chat_group`;
CREATE TABLE `wx_chat_group` (
  `id` int(11NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(64COLLATE utf8mb4_unicode_ci  NOT NULL DEFAULT '',
  PRIMARY KEY `id` (`id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_unicode_ci;

INSERT INTO `wx_chat_group` (`id``name`VALUES (1'德国IT职业信息分享群');

-- 每次抽取的不合规格的昵称将存储如表以供计数
DROP TABLE IF EXISTS `wx_chat_nickname_check`;
CREATE TABLE `wx_chat_nickname_check` (
  `id` BIGINT(20NOT NULL AUTO_INCREMENT,
  `group_id` int(9UNSIGNED NOT NULL,
  `wx_puid` VARCHAR(16COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `nickname` VARCHAR(64CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create time',
  PRIMARY KEY `id` (`id`),
  INDEX `idx_group_id` (`group_id`),
  INDEX `idx_create_time` (`create_time`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_unicode_ci;

2. 用户设置

所有用户自定义变量存入conf文件里,如群名、临时存储路径、数据库接入信息,踢人阈值:


 

[wechat]
group_name_1=德国IT职业信息分享群
group_id_1=1

path_tmp=/opt/tmp/

notice_random=5
kick_max=10
tuling_api_key=xxxxx

[mysql]
mysql_host=localhost
mysql_port=3306
mysql_user=root
mysql_pwd=xxxx
mysql_database=wechat_group_ibot

3. 监听群消息

初始化群聊对象,并且监听群消息


 

# 查找群聊,并且设置附加属性,以备后用
def init_group(group_name, group_id):
    group = ensure_one(bot.groups().search(group_name))
    group.ext_attr = lambdaNone
    setattr(group.ext_attr, 'group_id', group_id)
    setattr(group.ext_attr, 'group_name', group_name)
    return group

# 初始化微信机器人bot
bot = Bot(cache_path=True, console_qr=True)
# unique chat person's id
bot.enable_puid()

# 读取自定义参数
cf = configparser.ConfigParser()
cf.read('wechat.conf')
group_name_1 = cf.get('wechat''group_name_1')
group_id_1 = cf.get('wechat''group_id_1')

# 初始化群聊对象
group_1 = init_group(group_name_1, group_id_1)

# 监听类型为NOTE的群消息,如:"aa"邀请"bbb"加入了群聊
@bot.register(group_1, NOTE)
def welcome_for_group(msg):
    try:
        new_member_name = re.search(r'邀请"(.+?)"|"(.+?)"通过', msg.text).group(1)
    except AttributeError:
        return
    group_1.send(welcome_text.format(new_member_name, space_after_chat_at))


# 保持bot持续运行
bot.join()

4. 昵称检查

检查群友昵称,存入数据库并且发送提醒,  具体逻辑代码这里不予累述。


 

def check_nickname(nickname):
 # 正则检验群昵称是否标准
 if re.match(r'([一-龥]|[ -~]|[sS])+|([一-龥]|[ -~])+|([一-龥]|[ -~])+', nickname):
        return True
    else:
        return False

......

# 检查群友昵称
def process_group_members(group):
    # 每次检查前先刷新群成员信息,避免用户改了昵称后再次被提醒
    # 但刷新会改变成员临时的内部puid,所以检查昵称必须同时结合puid和nickname
    group.update_group(members_details=False)
    ......
    for member in group:
        nickname = member.name
        wx_puid = member.puid
        if not check_nickname(nickname):
            invalid_member = GroupMember(nickname, wx_puid, 0)
            invalid_members.append(invalid_member)

   .....
   # 随机抽取不合格的5人
   random_members = random.sample(invalid_members, k=5)
   ......


# 将本次提醒群友存入数据库,供下次计数
def insert_invalid_name(group_id, wx_puid, nickname):
    bot_db.execute("INSERT INTO wx_chat_nickname_check (`group_id`, `wx_puid`, `nickname`)"
                   " VALUES (%s, %s, %s)",
                   (group_id, wx_puid, nickname))

# 获取昵称不合规群友被提醒计数
def get_invalid_name_count(group_id, wx_puid, nickname):
    result = bot_db.get_count("SELECT id FROM wx_chat_nickname_check "
                              "WHERE group_id = %s and (wx_puid = %s or nickname = %s)", (group_id, wx_puid, nickname))
    return result

5. 数据库连接池

这里的数据库连接使用了数据库连接池:DBUtils.PersistentDB

DBUtils.PooledDB: 适用于多线程频繁开启关闭数据库连接

DBUtils.PersistentDB:适用于单线程多次频繁连接数据库

如果不采用线程池而是采取直连,那么运行一段时间后,脚本将出现该错误


 

pymysql.err.OperationalError: 2006

这里将DBUtils再次封装了一下,写了一个单例模式BotDatabase, 提供了query(select), execute(update, delete) 以及批处理execute等常用接口。

6. 启动定时器


 

# 早八点晚八点各执行检查一次
def start_schedule_for_checking_member(group):
    scheduler = BlockingScheduler()
    scheduler.add_job(lambda: process_group_members(group), 'cron', hour=8, minute=1, timezone="Europe/Paris")
    scheduler.add_job(lambda: process_group_members(group), 'cron', hour=20, minute=1, timezone="Europe/Paris")

最终成果

640?wx_fmt=jpeg   640?wx_fmt=jpeg

已知问题

在消息中输入 @群员昵称 并不能真正让该群友收到@提示(显示推送提示),微信App里是在@群员昵称后自动加上了一个特殊的显示空白的字符u’ ′。但是经测试,加上这个符号也不行,推测是微信Web API基于防范垃圾推送,屏蔽了群提示接口。

wxpy的bot在运行一段时间后会停止工作,出现连接服务器错误,必须重新登录,推测是微信Web API的Session安全机制导致的问题。

 

640?wx_fmt=png

数据清洗

 

一段时间后大部分群友修改了昵称,于是有了在德中国程序员职业和专业方向的数据,经清洗后,导出CSV规格如下。

640?wx_fmt=jpeg

 

640?wx_fmt=png

数据分析

 

该任务所需第三方库如下:


 

pip3 install pandas
pip3 install matplotlib
pip3 install jieba
pip3 install wordcloud
pip3 install seaborn
pip3 install palettable

开发需求

  • 在德程序员男女比例

  • 在德IT软件专业在职和学生比例

  • 分析在德程序员所处行业和专业方向

  • 程序猿和程序媛所处行业和专业方向对比

  • 分析在德程序员常用开发语言和框架

  • 程序猿和程序媛常用开发语言和框架对比

  • 在职和学生常用开发语言和框架对比

开发分解

1. 在德程序员男女比例,输出Pie Chart


 

    def gen_pie_member_gender(self, csv_file):
        df = pd.read_csv(csv_file, delimiter=' ', encoding='utf-8')

        genders = df['gender']
        col = [000]
        for g in genders:
            if g == 1:
                col[0] = col[0] + 1
            elif g == 2:
                col[1] = col[1] + 1
            else:
                col[2] = col[2] + 1

        perccent_male = '{0:.2f}%'.format((col[0]/len(genders) * 100))
        perccent_female = '{0:.2f}%'.format((col[1]/len(genders) * 100))
        perccent_unknown = '{0:.2f}%'.format((col[2]/len(genders) * 100))

        labels = [r'Male %s' % perccent_male,
                  r'Female %s' % perccent_female,
                  r'Unknown %s' % perccent_unknown]
        colors = ['lightskyblue''pink''gold']

        plt.figure(figsize=(86))
        patches, texts = plt.pie(col, colors=colors, startangle=90)
        plt.legend(patches, labels, loc="best")
        plt.title('Gender of Member')
        # Set aspect ratio to be equal so that pie is drawn as a circle.
        plt.axis('equal')
        plt.tight_layout()

        path_image = os.path.join(self.path_analyse,
                                  '%s_member_gender_pie.png' % self.group_id)

        plt.savefig(path_image, format='png', dpi=100)
        plt.close()
        return path_image

640?wx_fmt=png

分析:

在德中国程序猿和程序媛比率约为2:1,这个比例基本和中国籍蓝卡申请人男女比率持平。但是根据2018年中国程序员数据调查表,中国程序员群体中男女比例接近12:1。德国的各位猿,你们就偷乐吧。 

2. 在德IT软件专业在职人员和学生比例,输出Pie Chart

代码和上面雷同。

640?wx_fmt=png

分析:

IT信息行业在职工作人员和在读学生比率为9比1,绝大部分人是在职工作的。 

3. 在德程序员所处行业和专业方向,输出词云


 

    # 这里采用一个汉字停词库,近两千词
    @staticmethod
    def load_stopwords():
        filepath = os.path.join('./assets'r'stopwords_cn.txt')
        stopwords = [line.strip() for line in open(filepath, encoding='utf-8').readlines()]
        return stopwords

    def gen_wordcloud_info_nicknames(self, csv_file, column='branch', gender='all'):
        df = pd.read_csv(csv_file, delimiter=' ', encoding='utf-8')

        stopwords = set(STOPWORDS)
        stopwords.update(self.load_stopwords())
        # 可添加一些额外stopword,过滤词云结果
        stopwords.add('学生')

        #自定义jieba分词词库,定义一些IT软件特殊术语
        jieba.load_userdict("./assets/jieba_userdict.txt")

        col = df[column]
        # 将词云按限定图形布局
        shape_file = './assets/member_info_shape.png'

        word_count = ""
        for c in col:
            if c is not np.NaN:
                seg_list = jieba.cut(c, cut_all=False, HMM=True)
                for word in seg_list:
                    word_count = word_count + word + " "

        mask = np.array(Image.open(shape_file))
        font = r'./assets/heiti.ttf'

        word_cloud = WordCloud(
            margin=0,
            mask=mask,
            font_path=font,
            scale=1,
            stopwords=stopwords,
            random_state=42,
            background_color='white'
        ).generate(word_count)

        path_image = os.path.join(self.path_analyse,
                                  '%s_member_word_cloud_%s_%s.png' % (self.group_id, column, gender))

        word_cloud.to_file(path_image)

        return path_image

640?wx_fmt=png

分析:

  1. 大数据,数据分析,数据挖掘

  2. 机器学习ML,人工智能AI,深度学习

  3. 汽车,自动驾驶,CV(机器视觉)

这三个大方向占比最大,说明中国码农在德国还是跟得上软件信息时代的变革的,并没有像一般德国码农那样一个技术吃一辈子。基于德系汽车制造业在电动车和自动驾驶领域的研发投入加重,越来越多的程序员也向这几个领域转型。一些传统企业如制造业,也开始用大数据来发现生产中潜在的工艺改进,或提前故障预警。

云计算,和以上三个方向密切相关,从业人员却不多。这可能是因为德国本土鲜有自己的大规模云计算服务商,很多企业没有自己的私有云,而将云服务部署在AWS上,如AWS就和奔驰,大众等汽车厂商达成了云数据等方向的深度合作。

互联网,电商这些领域在我另一篇文章中国程序员在德国里提过,在德国属于荒漠地带,从业人员数量少可以预见。

咨询和SAP这两个领域,在德国企业里可以说是四平八稳,很多德国传统企业的IT项目多为外包,自己的IT团队只负责管理和规划,所以专业咨询人员必不可少。同时SAP系统在德国各行业的占有率非常高,而且SAP的定制功能强大,包罗万象,可以说,如果不考虑开发时间和成本,你想要什么流程,SAP都能给你二次开发出来。

经济、金融、银行,由于英国脱欧,大批金融机构从伦敦搬到法兰克福,对程序员也是求贤若渴。

图中还出现了区块链的身影。 

4. 程序猿和程序媛所处行业和专业方向对比,导出云图

处理步骤和上述雷同,所以将gen_wordcloud_info_nicknames进行扩展,按branch或者language过滤数据源。


 

......

        if gender == 'male':
            df_male = df[df.gender == 1]
            col = df_male[column]
            shape_file = './assets/member_%s_%s_shape.png' % (column, gender)
        elif gender == 'female':
            df_male = df[df.gender == 2]
            col = df_male[column]
            shape_file = './assets/member_%s_%s_shape.png' % (column, gender)
        else:
            col = df[column]
            shape_file = './assets/member_info_shape.png'
......

生成图片:

640?wx_fmt=png

嗯?这是什么鬼,这两坨哪里能看出男女区别了?!

作为有着钢铁直男审美的程序员决不能容忍这么丑陋的数据呈现,好在Python支持透明PNG图片叠加,先准备相应的Alpha透明度较高的图片。


 

        # 加载透明背景图片
        if gender == 'male':
            ......
            shape_alpha_file = './assets/member_%s_%s_shape_alpha.png' % (column, gender)
        elif gender == 'female':
            ......
            shape_alpha_file = './assets/member_%s_%s_shape_alpha.png' % (column, gender)
        else:
            ......
            shape_alpha_file = './assets/member_info_shape_alpha.png'        
       ......

       # 预定义词云输出颜色集范围,突出男女有别
       if gender == 'male':
            word_cloud.recolor(color_func=self.color_func_blue, random_state=3)
        elif gender == 'female':
            word_cloud.recolor(color_func=self.color_func_red, random_state=3)

        word_cloud.to_file(path_image)

        # 将透明背景图叠加到云图上
        background = Image.open(path_image)
        foreground = Image.open(shape_alpha_file)
        background.paste(foreground, (0, 0), foreground)
        background.save(path_image)

640?wx_fmt=png

这下数据呈现美观且直观多了。

分析:

  • 在大数据,数据挖掘分析,机器学习上,猿媛平分秋色

  • 自动驾驶和机器视觉CV,猿占比稍微多一点

  • 媛更爱ERP、CRM、VWL、仓管、MES等企业级软件系统领域,还有BI和SAP领域

  • 软件测试少不了程序媛妹子

  • 比较意外的是从事嵌入式开发的女汉子不少

  • 猿从事的专业领域更广,不过这也可能是采样数据不够大的原因。 

5.  在德程序员常用开发语言和框架

下面这三个分析代码一致,这里只贴出数据结果。

5.1 在德程序员常用开发语言和框架

640?wx_fmt=png

分析:

  • 得益于大数据和AI的火热,Python当仁不让位居第一。

  • Java、C和C++并驾齐驱。

  • Java是德国传统企业里企业级应用系统开发语言首选,出于系统稳定和兼容性考虑

  • C++和C在自动驾驶,医疗,机械,嵌入式系统领域是不可替代的

  • 当然不能忘了.Net在工业界PLC系统里基本是霸主地位

  • Angualr、React、Vue、jQuery、TypeScript在企业级应用前端开发里使用广泛

  • 移动开发iOS和Android基本绝迹,原因之前也提了。

以上占比分布基本是符合德国六个行业开发语言占比率的,依次为软件开发,信息技术,电商,汽车,咨询,金融。稍微反常的是德国的中国程序员在工作使用PHP的不多,可是会PHP在德国也是很容易找工作的嘛。

640?wx_fmt=png

5.2 程序猿和程序媛常用开发语言和框架对比

640?wx_fmt=png

分析:

  • 猿偏向后端开发,媛偏向前端开发

  • 在Java、C++、C上,猿媛旗鼓相当

  • 在数据库管理和SQL使用上,媛稍微多一点

总体来说,在德程序媛的能力并不比程序猿弱,技术栈相当广,德国不愧是培养理工女汉子的国度。 

5.3 在职人员和学生常用开发语言和框架对比

640?wx_fmt=png

分析:

加上这个对比,是因为之前担心一些热门语言是因为在读学生用得多,才占比高。不过从这个图看来,程序员们在工作中是确确实实使用到了这些技术栈,并不只停留在大学和科研领域。

 

640?wx_fmt=png

在德中国程序员说什么:聊天数据分析

 

以上知道了在德中国程序员们做什么,那么来看看他们平时聊什么?

  • 数据采集

  • 开发需求

  • 监听并记录群聊入库

开发分解

1. 首先建表


 

DROP TABLE IF EXISTS `wx_chat_history`;
CREATE TABLE `wx_chat_history` (
  `id` BIGINT(20NOT NULL AUTO_INCREMENT,
  `group_id` int(9UNSIGNED NOT NULL,
  `msg_type` VARCHAR(16COLLATE utf8_unicode_ci NOT NULL DEFAULT 'Text',
  `wx_puid` VARCHAR(16COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `gp_user_name` VARCHAR(70COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `sender_name` VARCHAR(64CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  `receiver_name` VARCHAR(64CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  `msg` VARCHAR(2048CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create time',
  PRIMARY KEY `id` (`id`),
  INDEX `idx_group_id` (`group_id`),
  INDEX `idx_create_time` (`create_time`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_unicode_ci;

2. 数据持久化

通过监听群消息,将获取的消息录入数据库。


 

# 将puid,nickname,消息等相应数据存入数据库
def insert_chat_history(group_id, msg_type, wx_puid, gp_user_name, sender_name, receiver_name, msg):
    bot_db.execute("INSERT INTO wx_chat_history (`group_id`, `msg_type`, `wx_puid`, "
                   "`gp_user_name`, `sender_name`, `receiver_name`, `msg`)"
                   " VALUES (%s, %s, %s, %s, %s, %s, %s)",
                   (group_id, msg_type, wx_puid, gp_user_name, sender_name, receiver_name, msg))


def save_message(msg, group_id):
    # create_time = msg.create_time.strftime('%Y-%m-%d %H:%M:%S')
    member_name = msg.member.name
    wx_puid = msg.member.puid
    gp_user_name = msg.member.user_name
    message = ''
    # 常规消息
    if msg.type == TEXT:
        message = msg.text
    # 分享链接
    elif msg.type == SHARING:
        art_list = msg.articles
        for item in art_list:
            print(item.url + ' ' + item.title + ' ' + item.summary)
            message = item.url + '||' + item.title + '||' + item.summary

    insert_chat_history(group_id, msg.type, wx_puid, gp_user_name, member_name, '', message)


# 监听群聊,包含自己发送的消息
@bot.register(group_1, except_self=False)
def reg_msg_for_group(msg):
    save_message(msg, group_id_1)

# keep login by block thread
bot.join()

 

640?wx_fmt=png

数据清洗

 

定义函数,可以从数据库里提取指定时间段(如一个月)的数据,并生成csv以供下一步使用。


 

    def save_chat_in_current_month(self, group_id):
        results = self.load_chat_history(group_id, self.fl_days[0], self.fl_days[1])
        path_csv_file = os.path.join(self.path_analyse,
                                     '%s_chat_%s_%s.csv' % (self.group_id, self.fl_days[0], self.fl_days[1]))

        with open(path_csv_file, mode='w', encoding='utf-8') as csv_file:
            fieldnames = ['id''create_time''msg_type''wx_puid''sender_name''msg']
            csv_writer = csv.writer(csv_file, delimiter=' ', quotechar='"', quoting=csv.QUOTE_MINIMAL)

            csv_writer.writerow(fieldnames)
            for row in results:
                row_id = row[0]
                msg_type = row[1]
                wx_puid = row[2]
                sender_name = row[3]
                msg = row[4]
                create_time = row[5]
                msg = self.format_message(msg)
                csv_writer.writerow([row_id, create_time, msg_type, wx_puid, sender_name, msg])

        csv_file.close()

        return path_csv_file

最终生成的CSV格式文件:

640?wx_fmt=jpeg

 

640?wx_fmt=png

数据分析

 

开发需求

  • 生成话题词云

  • 分析消息种类占比

  • 分析日均聊天曲线

  • 分析群员聊天活跃时间热点图

开发分解

1. 话题词云

相关代码和上面相近,生成的云图:

640?wx_fmt=png

分析:

德国中国两者工作生活的对比是永恒的话题,到底回国还是留德,经常是热点。

因为是职业群,所以大部分话题还是集中在职场:公司、工作、老板、工资、技术

IT领域不得不提领头羊美国,包括硅谷的工资。

讨论贸易战少不了华为

创业目前在留德华中也是个热门话题 

2. 消息种类占比,生成Bar Chart


 

    def gen_bar_plot_msg_type(self, csv_file):
        df = pd.read_csv(csv_file, delimiter=' ', encoding='utf-8')

        df['msg_type'].value_counts().plot(kind='bar')

        plt.subplots_adjust(bottom=0.2)
        plt.title('Message Type [%s - %s]' % (self.fl_days[0], self.fl_days[1]))
        path_image = os.path.join(self.path_analyse,
                                  '%s_chat_msg_type_bar_%s_%s.png' % (self.group_id, self.fl_days[0], self.fl_days[1]))
        plt.savefig(path_image)
        plt.close()

        return path_image

640?wx_fmt=jpeg

分析:

聊天以文字信息为主,没有出现其他灌水群的斗图行为。 

3. 日均聊天频率,生成Bar Chart


 

def gen_bar_plot_chat_freq_day(self, csv_file):
        df = pd.read_csv(csv_file, delimiter=' ', encoding='utf-8')
        msg_count = len(df)

        time_list = self.cal_time_list_chat_freq_day(df)

        plt.figure(figsize=(189))
        plt.bar(time_list.keys(), time_list.values(), width=.8, facecolor='lightskyblue', edgecolor='white')
        plt.xticks(range(len(time_list)), time_list.keys())
        for x_axies in time_list:
            y_axies = time_list[x_axies]
            label = '{}%'.format(round(y_axies*1.0/msg_count*1002))
            plt.text(x_axies, y_axies+0.05, label, ha='center', va='bottom')
        plt.title('Chat frequency in 24 hours [%s - %s]' % (self.fl_days[0], self.fl_days[1]))
        path_image = os.path.join(self.path_analyse,
                                  '%s_chat_freq_day_bar_%s_%s.png' % (self.group_id, self.fl_days[0], self.fl_days[1]))
        plt.savefig(path_image)
        plt.close()

        return path_image

640?wx_fmt=jpeg

分析:

  • 每日从六点开始活跃,估计是一部分人上班通勤坐车时有时间聊聊

  • 早七八点到公司开始工作,安静

  • 早九、十点开始活跃,到午休11点左右到达高峰

  • 午休后工作时间

  • 下午三点开始活跃,这时是德企里的下午茶时间

  • 晚9点饭后再次活跃一下 

4. 群员聊天活跃时间周热点图,  输出Heat Map


 

    def gen_heatmap_member_activity(self, csv_file):
        df = pd.read_csv(csv_file, delimiter=' ', encoding='utf-8')
        create_times = df['create_time']

        week_online = [[0 for j in range(24)] for i in range(7)]
        for li in create_times:
            week_online[int(mk_datetime(li, "%Y-%m-%d %H:%M:%S").weekday())][int(li[11:13])] += 1

        week_online = np.array([li for li in week_online])
        columns = [str(i) + '-' + str(i + 1for i in range(024)]
        index = ['Mon.''Tue.''Wed.''Thu.''Fri.''Sat.''Sun.']

        week_online = pd.DataFrame(week_online, index=index, columns=columns)
        plt.figure(figsize=(18.59))
        plt.rcParams['font.sans-serif'] = ['SimHei']
        sns.set()

        # Draw a heatmap with the numeric values in each cell
        sns.heatmap(week_online, annot=True, fmt="d", cmap="YlGnBu")
        path_image = os.path.join(self.path_analyse,
                                  '%s_activity_heatmap_%s_%s.png' % (self.group_id, self.fl_days[0], self.fl_days[1]))
        plt.savefig(path_image, format='png', dpi=300)
        plt.close()

        return path_image

640?wx_fmt=png

分析:

  • 周一大家都很忙,或者装着很忙的样子

  • 周二下午开始活跃了

  • 周三上午也活跃起来

  • 周四,快到周末了,放松,全天活跃

  • 周五,上午欢乐时光,下午和德国同事一样,走的走跑的跑

  • 周末死一般沉寂

从这个分析图可以看出,中国程序员上班是非常用心和责任感的,同时也非常遵守德企工作时间相关制度,坚决不加班,坚决朝九晚五。

996是什么?能吃吗?

认真地说,为工作和任务有限加班是可以的,但我非常反对无效的为加班而加班,把996作为KPI考勤标准的做法。

德国大中型企业一般做法是员工自行调配加班时间,某段时间任务紧,加班时间多了的话可以将超时存起来,之后再换成休假;实在没空休假的可以换成工资,不过一般HR和工会不推荐这么做,超时太多将强制休假—-员工健康比工作重要。

在德企小公司或咨询公司里,就不一定了,因为可能业绩和分红挂钩,或者小公司项目紧张,主动或被动加班是很常见的。

绝大多数IT企业并不固定员工的上下班时间,而是采用核心时间制度,比如10-15这五个小时员工必须在岗,但员工可以自行决定上班和下班时间,早来早走,晚来晚走,自由度高。

 

640?wx_fmt=png

制作PDF总报表

 

以上数据分析步骤生成了若干独立的图片报表,不便传阅,可以将其集中整理并且排版格式化到一个PDF总报表里,方便阅读。

所需第三方库如下,可以将含图片的html页面完整输出成PDF文件。

5G之后,中国的人工智能发展趋势如何?

https://edu.csdn.net/topic/ai30?utm_source=csdn_bw


 

pip3 install pdfkit

Install wkhtmltopdf

Debian/Ubuntu:
> sudo apt-get install wkhtmltopdf

Redhat/CentOS
> sudo yum install wkhtmltopdf

MacOS
> brew install Caskroom/cask/wkhtmltopdf

开发分解

1. 准备HTML模板


 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style>
        h1 {
            text-align: center;
        }
        h2 {
            text-align: center;
            margin-top20px;
        }
        img {
            display: block;
            margin0 auto;
        }
    </style>

</head>
<body>

<h1>{{group_name}} 聊天数据分析</h1>
<h2>{{date_begin}} - {{date_end}}</h2>

<h2>24小时内聊天频率</h2>
<img src="{{img_chat_freq_day}}" style="width:100%;"/>

<h2>消息类型</h2>
<img src="{{img_chat_msg_type}}" style="width:80%;"/>

<h2>日均聊天数量</h2>
<img src="{{img_chat_count_day}}" style="width:100%;"/>

<h2>群友活跃时间热点图</h2>
<img src="{{img_chat_heating_act}}" style="width:100%;"/>

<div class="divider_b"></div>
......

2. 生成PDF

读取HTML模板,替换Pattern,生成PDF


 

        with open('./assets/chat_analysis_%s.html' % lang, 'r'as file:
            file_data = file.read()

        # 替换Pattern
        file_data = file_data.replace('{{date_begin}}', self.fl_days[0])
        file_data = file_data.replace('{{date_end}}', self.fl_days[1])

        file_data = file_data.replace('{{img_chat_history}}', Path(img_chat_history).name)
        file_data = file_data.replace('{{img_chat_freq_day}}', Path(img_chat_freq_day).name)
        ......

       # 输出临时HTML文件
       with open(path_html, 'w'as file:
            file.write(file_data)
       # 输出pdf
       pdfkit.from_file(path_html, path_pdf)

3. 定时任务

添加定时任务,每月第一天早八点自动启动数据分析任务,分析上个月数据,然后自动将PDF报表发到群里。


 

    # 08:10am at the first day of the month
    scheduler.add_job(lambda: process_schedule(bot_db, bot, group_1), 'cron',
                      month='1-12', day=1, hour=8, minute=1, timezone="Europe/Paris")

    # 发送文件到指定群里
    group.send_file(file_path)

最终PDF报表预览:

640?wx_fmt=png

 

640?wx_fmt=png

总结

 

通过数据分析可以非常直观地了解工作和生活在德国的中国程序员们,平时做什么工作,说什么话题。不过因为采样数量较小,某些分析无法采用更明确的类别数量占比图,分析结果难免有偏差,还请见谅。

本文使用Python和相关库快速完成了数据采集,清洗和分析的工作,你可以基于该项目,扩展自己的数据分析模块,比如图灵聊天机器人,连接各类第三方服务。

项目源代码已上传至 GitHub,欢迎指教和加星。 

PS:@将记忆深埋 究竟在不在这几个IT群里,始终是个谜。 

参考资料:

Figures on the EU Blue Card

76.000 Ausländer mit “Blauer Karte” in Deutschland

Der Stack Overflow Entwicklerreport 2017

2018年中国程序员数据调查表

 

源码地址:https://github.com/mobabel/wechat-group-ibot

注:文中聊天数据只短时间采集并供本文脱敏分析,后期清除不再继续监听。

作者简介:李辉,德国硕士毕业后,在软件咨询业工作多年,涉猎全栈及移动开发构架。

现从业物联网,在德国某一级汽车零配件供应商任高级软件工程师。德中工业4.0学会理事会理事。

【END】

CSDN 5G免费沙龙来啦!

6月29日,微软(中国)首席技术官韦青、北京邮电大学信息与通信工程学院多媒体技术教研中心主任/博士生导师孙松林、爱立信中国研发部多天线高级专家朱怀松、爱立信中国研发部主任系统工程师刘阳等行业内顶尖的领军者、资深的技术专家们共聚一堂,共同探讨5G在物联网中的巨大潜能。

扫描下方二维码,即刻免费报名!

640?wx_fmt=jpeg

 热 文 推 荐 

鸿蒙将至,安卓安否?

QQ 小程序来了,怎么做?

微软发布 VS Code Java 安装程序,一键安装所有 Java 开发环境

Docker 存储选型,这些年我们遇到的坑

☞荔枝自由?朋友,你实现了吗?

开源要自立?华为如何“复制”Google模式

☞从制造业转型物联网,看博世如何破界

回报率850%? 这个用Python优化的比特币交易机器人简直太烧脑了...

☞老码农冒死揭开编程黑幕:这些Bug让我认输,谁踩谁服!

640?wx_fmt=gif点击阅读原文,即刻免费报名 5G 沙龙!

640?wx_fmt=png你点的每个“在看”,我都认真当成了喜欢

展开阅读全文

印度程序员vs中国程序员

06-01

> 印度软件开发rn> rn> 我在工作中,接触到印度软件公司开发出来的软件: rn> 整个体系架构非常清晰,按照我们的要求实现了全部功能,而且相当稳定。但是打开具体的代码一看,拖沓冗长,水平不咋样。我们自己的一些程序员就有怪话了,说他们水平真低。但是! 印度人能够把软件整体把握得很好,能够完成软件,并得到相当好的设计文档。 而中国人在那里琢磨数据结构、算法,界面人员就还没编码就想着是Outlook式的还是Visual Studio式的界面。到最后就成为Code高手,对某些特定的开发工具精通,但是就是不能保证能够把一个软件稳当、完整的开发出来。 rn> 举个简单的例子: rn> 软件中需要一个列表,用来表示我们处理的事务。该类表在业务繁忙的时候将变得很大。中国人就用双向链表,抱着《数据结构》书在那里写链表的类。印度人开了一个大数组,然后就开始干。为什么印度人不用链表,他们说: rn> 1、你们给出的设备(小型机),最少具备512M内存,浪费一些没有什么。 rn> 2、数组方式访问方便、效率高。 rn> 看出了一拿到东西就吭哧吭哧作Code,和好好进行软件分析的不同了吗? rn> 正好前几天我有几个同事从印度回来和我们交流,那家公司是CMM4级公司. 我感受的几点:rn> 1,流程重于项目 rn> 2,QC(就是QA)独立于研发部门,专门检查研发部门的开发流程是不是按照既定流程走.如果QC觉得流程不对,他会直接上报高层,项目肯定就此停止. rn> 3,所谓的项目经理(PC)一般也是从编码人员升上来的,并不是所谓的不懂技术,一般都至少有四年以上的经验 rn> 4,PC主要就是制定开发计划,负责协调,填写各种表格.rn> 5,所有的东西(包括草稿)都有文档.rn> 6,详细文档要求达到只有这个文档就可以编码的程度,一般写文档时间占60%,编码时间极少 7,有各种详细的review(同行评审),项目组内的,项目组之间的,客户的... rn> 8,计划很详细,的确能达到小时级,但是实际情况还是误差比较大,所以他们也有加 班. rn> 先学习UML和Rose以及RUP,不要总是要找着证据。 在中国的软件开发水平下,很难给你一个好的例子,OK? 中国人总是要看到一个东西有了试验田,而且稻子长得好,才换稻种。要知道在国外上述的软件开发模式的应用,大可以看看Rational网页上的story。Just do it! 一句话,中国的软件开发水平低得很。赶不上印度人,印度的软件公司可以让高中生编代码,它的软件工程水平可想而知。 当然,你如果是个很牛的程序员。估计够呛,因为中国的气氛中,很牛的程序员都很难接受软件工程的。你可以测试一下自己,看看自己适不适合现在学习软件工程: rn> 1、你是不是不能忍受一个编程序不如你的人做你的项目经理?rn> 2、你是不是觉得你的老板对客户吹牛皮、夸大自己而感到不舒服?rn> 3、你是不是一个拿到一个需求脑袋里第一念头就是如何实现的人?rn> 4、你是不是很崇拜Stallman,Linus,很讨厌Microsoft? rn> 5、你是不是曾经在深夜编码的时候,突然感觉到一种乏味,对Code的生涯感到一种无趣? 以管窥豹──印度神话 作者:"Kino" 我们现在处于深深的自卑当中,感到中国的软件工程水平的低下已经是牵涉到民族劣根性的问题了。 rn> 1、他们的软件教育水平: 我们招聘印度人,给应聘者出了一份与国内差不多的试卷,有基础概念和编程题目。 等到他们完成后,我们这些中国的自认高手惊呆了!他们的编程题目简直象是抄袭的。 程序结构,注释,变量命名就不说了吧,全部都是极其类似! 反观中国的牛人、高手,每个人有自己的一套。到了新的岗位,先把前任的程序贬损一通,然后自己再开发更多的问题的代码来代替。我的公司统计,一个软件中有4个以上 CSocket版本,每个人都觉得别人做得差,自己再搞一套。中国人,就是这个样子,还会辩解说“我们这样有创造性”。 其实软件发展,早就走过了求伯君那个编码英雄的年代,程序员已经是个坐办公室的蓝领了。你具备拧好一个螺丝钉的能力就可以了。Code是最低级的事情了。 rn> 2、他们许多公司的项目经理根本就不懂技术。 中国的项目经理如果不能在技术上压服下属,那么下属将与他搞鬼,越是高手越喜欢搞鬼,根本不知道作软件的终极目的是从别人兜里掏钱,而在内部搞不团结。技术高手都会纠集一些对他技术上崇拜的菜鸟,与管理层作对。而印度的软件经理根本就不懂正在做的东西,许多甚至直接就是MBA,或者是领域专家 (工业设计、地理专家等),而不是编码的专家。但是却能够领导大群素质良好的程序 员把工作做好,没有内部不团结的情况。许多印度的程序员加入一个公司很长时间,都不知道自己整天编的代码是干什么用的。给他们的任务可能就是一个函数的声明以及该函数要实现的功能。我们呢? rn> 3、他们的编程人员的流动率达到30%! 他们的编程人员流动率(包括内部项目之间的流动)高达30%,可以想见他们的文档水 平如何。他们的产品不依赖任何一个人,谁都可以立即辞职,产品的开发还是会正常进行。 而中国,是老板怕总工。技术骨干拥兵自重,抗拒管理。任何制定好的计划,都有可能被技术人员推翻或者跟你消极怠工。 rn> 4、他们的开发计划能够做到小时级别。 如果一个印度公司的项目经理没有上班,那么他的下属将可能不知道作什么。他们的计 划一般都定到天,每个基层开发人员每天的工作量就是8小时。 而我们能够给出月度计划的公司就很少,而给出的月度计划要么不可能实现,要么就可 能被取消。开发人员被初略的给个任务,他在月初,可以慢慢琢磨是做成什么样子,然 后上上网,聊聊天。到了月中和月末,就开始熬夜编码。 rn> 看到每年,从各大高校不尽牛人滚滚来,我们是不得不要召人,同时又是不抱希望。我 公司现在有意以后将核心软件开发外包给印度公司,中国人?做做界面吧,中国人做界 面会极尽奇技淫巧,搞得花里胡哨的。 BTW,我公司非外企,大家不要误会我们有什么种族歧视。但是我们现在就是对自己歧 视,自卑得很。中科院那么多研究院,连个能用的操作系统都搞不定。北大开发一些东 西,比如什么青鸟CASE,就是给一帮人评职称的。杨芙清院士整天搞来搞去,搞出了什 么东西?B大,T大的人最难管理,牛得看不见人。 中国的程序员骂微软,追Linux是全世界最狠的,可是我们除了汉化Linux,做了什么东 西出来。CDE是瑞典人写的,Linus是芬兰的,GNome是墨西哥人写的。哎,我们曾经是多么的瞧不起印度人。 rn 论坛

没有更多推荐了,返回首页