2024年4月

在前端代码中很经常看到使用
setTimeout(fn, 0)
,如下面代码所示,乍一看很多余,但是移除了可能会出现一些奇奇怪怪的问题。要解释这个就需要理解
事件循环(Event Loop)
,下面会通过一些例子和动画来辅助理解事件循环

setTimeout(() => {
  // 调用一些方法
}, 0)

为什么使用事件循环

JS 是单线程的(
浏览器和 Node则是多线程的
),为了避免
渲染主线程
阻塞,需要异步,
事件循环
是异步的实现方式

浏览器在一个渲染主线程中运行一个页面中的所有 JavaScript 脚本,以及呈现布局,回流,和垃圾回收。为了避免
同步
的执行方式导致渲染主线程阻塞,使得页面卡死,所以浏览器采用异步的方式:渲染主线程将任务交给其他线程去处理,自身
立即结束
任务的执行,转而执行后续代码,当其他线程完成时,将事先传递的回调函数包装成任务,加入到对应的消息队列的
末尾
排队,等待渲染主线程调度执行

流程:

  1. 渲染主线程执行全局 JS,需要异步的任务放到对应的队列,如果是
    setTimeout
    则会有线程计时,到了指定时间会将任务放入
    延时队列
    (并非立即执行)
  2. 渲染主线程为空时,按队列的优先级依次选择队列(最先执行微队列的任务),依次按顺序执行各个队列的任务

任务没有优先级,而消息队列有优先级,不同任务分属于不同队列:
参考 W3C 规范

微队列优先级最高
,接着是交互队列然后才是延时队列

常见队列:

  • 微队列(microtask):⽤户存放需要最快执⾏的任务,优先级「最⾼」,通过
    Promise.resolve().then()
    ⽴即把⼀个函数添加到微队列
  • 交互队列:⽤于存放⽤户操作后产⽣的事件处理任务,优先级「⾼」
  • 延时队列:⽤于存放计时器到达后的回调任务,优先级「中」

事件循环

下面例子来自于:
《WEB前端大师课》
,大块的文字描述相对没那么直观,所以用 Keynote 做了 gif 方便理解(如果有更好的做 gif 的方式可以留言告诉我)

1. JS阻碍页面渲染

JS 修改了 DOM 后,并不会马上显示在页面上,需要进行
绘制
后才会显示页面变更

<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <h1>初始h1</h1>
    <button>change</button>
    <script>
      var h1 = document.querySelector('h1');
      var btn = document.querySelector('button');

      function delay(duration) {
        var start = Date.now();
        while (Date.now() - start < duration) {}
      }
      
      btn.onclick = function () {
        h1.textContent = '修改h1 textContent';
        delay(3000);
      };
    </script>
  </body>
</html>

01绘制任务

效果:点击
change
后,页面卡死,3s 后
h1
内容变更为:
修改h1 textContent

2. 延迟队列

setTimeout
到达指定时间可能并不会立即执行

setTimeout(function () {
  console.log(1);
}, 0);

function delay(duration) {
  var start = Date.now();
  while (Date.now() - start < duration) {}
}

delay(3000);

console.log(2);

02settimeout

效果:卡死 3s 后输出 2 1

3. 微队列

使用
Promise.resolve().then
可以将任务直接添加到微队列

setTimeout(function () {
  console.log(1);
}, 0);

Promise.resolve().then(function () {
  console.log(2);
});

console.log(3);

03微队列

效果:依次输出 3 2 1

4. 复杂情况

function a() {
  console.log(1);
  Promise.resolve().then(function () {
    console.log(2);
  });
}
setTimeout(function () {
  console.log(3);
  Promise.resolve().then(a);
}, 0);

Promise.resolve().then(function () {
  console.log(4);
});

console.log(5);

04复杂Promise

效果:依次输出 5 4 3 1 2

拓展

理解了上面的概念,可以尝试分析一下
现代 JavaScript 教程 事件循环例子
,检查一下是否理解了事件循环

参考资料

2024 年我还在写这样的代码
为什么 JS 要加入 setTimeout, css 的 transition 才能生效
深入理解和使用 Javascript 中的 setTimeout(fn,0)
主线程
并发模型与事件循环
异步 JavaScript
调度:setTimeout 和 setInterval

前言

今天大姚给大家分享一款基于Material Design风格开源、免费(MIT License)、易于使用、强大的WPF UI控件库:
MaterialDesignInXamlToolkit

项目介绍

MaterialDesignInXamlToolkit 是一个开源、易于使用、强大的 WPF UI 控件库,旨在帮助开发人员在 C# 和 VB.Net 中实现 Google 的 Material Design 风格的用户界面。该框架提供了一组丰富的控件、样式和效果,使开发人员能够轻松创建现代化、具有吸引力的应用程序。

WPF介绍

WPF 是一个强大的桌面应用程序框架,用于构建具有丰富用户界面的 Windows 应用。它提供了灵活的布局、数据绑定、样式和模板、动画效果等功能,让开发者可以创建出吸引人且交互性强的应用程序。

运行项目源码

设置
MaterialDesignDemo
为启动项目运行查看:

项目运行截图

项目源码地址



更多项目实用功能和特性欢迎前往项目开源地址查看

引言

上一章我们介绍了在
xUnit
单元测试中用
xUnit.DependencyInject
来使用依赖注入,上一章我们的
Sample.Repository
仓储层有一个批量注入的接口没有做单元测试,今天用这个示例来演示一下如何用
Bogus
创建模拟数据 ,和
EFCore
的种子数据生成

Bogus 的优势

  1. 丰富的数据生成支持
    :Bogus 提供了广泛的 API 支持,涵盖了各种数据类型和用例,使得生成虚假数据变得非常灵活和方便。

  2. 重复性和可控性
    :通过设置种子值,可以确保生成的虚假数据是可重复的,这对于需要一致的测试数据或示例数据非常有用。

  3. 易于使用
    :Bogus 使用流畅的语法和简单的方法调用,使得生成虚假数据变得简单直观,即使是对库不熟悉的用户也可以快速上手。

  4. 内置规则和语义
    :内置了许多常见数据类别的规则和语义,例如公司名称、产品名称、地址等,可以快速生成符合实际场景的数据。

  5. 灵活性
    :除了内置规则外,还可以通过自定义规则来生成特定的数据,满足不同场景下的需求。

  6. 社区支持
    :Bogus 是一个受欢迎的开源库,拥有活跃的社区支持和维护,可以获得持续的更新和改进。

Bogus 实战

简介

Bogus
是一个简单的
.NET
语言(如
C#

F#

VB.NET
)的假数据生成器。
Bogus
本质上是
faker.js

C#
移植版本,并受到
FluentValidation
的语法糖的启发。

使用

创建新的
xUnit
测试项目
dotNetParadise.Bogus

Nuget
包安装
Bogus

Install-Package Bogus


创建一个`Bogus`帮助类
```c#
using Bogus;
using Sample.Repository.Models;

namespace dotNetParadise.Bogus
{
    public class BogusHelper
```package manager
PM> NuGet\Install-Package Bogus -Version 35.5.0

和上一篇的配置一样,测试项目需要添加仓储层的项目引用,并通过
Nuget
安装
xUnit.DependencyInject
,配置
Startup

先看一下我们的
Staff
实体

public class Staff
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int? Age { get; set; }
    public List<string>? Addresses { get; set; }
    public DateTimeOffset? Created { get; set; }
}

接下来对我们批量新增的接口进行单元测试,测试数据通过
Bogus
生成,先看使用在讲解用法。

生成
500
条测试数据保存到 DB

[Fact]
public async Task BatchAddStaffAsync_WhenCalled_ShouldAddStaffToDatabase()
{
    // Arrange
    var staffs = new Faker<Staff>()
        .RuleFor(u => u.Name, f => f.Person.FullName)
        .RuleFor(u => u.Email, f => f.Person.Email)
        .RuleFor(u => u.Age, f => f.Random.Number(18, 60))
        .RuleFor(u => u.Addresses, f => f.MakeLazy(f.Random.Number(1, 3), () => f.Address.FullAddress()))
        .RuleFor(u => u.Created, f => f.Date.PastOffset())
        .Generate(500);

    // Act
    await _staffRepository.BatchAddStaffAsync(staffs, CancellationToken.None);

    // Assert
    var retrievedStaffs = await _staffRepository.GetAllStaffAsync(CancellationToken.None);
    Assert.NotNull(retrievedStaffs); // 确保 Staff 已成功添加到数据库
    Assert.Equal(500, retrievedStaffs.Count); // 确保正确数量的 Staff 已添加到数据库
    Assert.True(staffs.All(x => retrievedStaffs.Any(_ => x.Id == _.Id)));
}

看代码配置跟
FluentValidation
都是一样都是通过
RuleFor
来配置实体的属性

看一下生成的测试数据

image

Run Tests

image

单元测试成功,有了
Bogus
之后我们创建一些测试数据就方便多了

Bogus 的用法

locales 国际化

Bogus
支持许多不同的地区设置(
locales
),这些地区设置可用于生成特定语言或地区的虚假数据。您可以通过设置不同的
locale
参数来使用不同的地区设置。

Bogus
支持以下地区设置(
locales

Locale Code Language Locale Code Language
af_ZA Afrikaans fr_CH French (Switzerland)
ar Arabic ge Georgian
az Azerbaijani hr Hrvatski
cz Czech id_ID Indonesia
de German it Italian
de_AT German (Austria) ja Japanese
de_CH German (Switzerland) ko Korean
el Greek lv Latvian
en English nb_NO Norwegian
en_AU English (Australia) ne Nepalese
en_AU_ocker English (Australia Ocker) nl Dutch
en_BORK English (Bork) nl_BE Dutch (Belgium)
en_CA English (Canada) pl Polish
en_GB English (Great Britain) pt_BR Portuguese (Brazil)
en_IE English (Ireland) pt_PT Portuguese (Portugal)
en_IND English (India) ro Romanian
en_NG Nigeria (English) ru Russian
en_US English (United States) sk Slovakian
en_ZA English (South Africa) sv Swedish
es Spanish tr Turkish
es_MX Spanish (Mexico) uk Ukrainian
fa Farsi vi Vietnamese
fi Finnish zh_CN Chinese
fr French zh_TW Chinese (Taiwan)
fr_CA French (Canada) zu_ZA Zulu (South Africa)

有些地区设置可能没有完整的数据集,比如说,有些语言可能缺少某些数据集,例如中文(
zh_CN
)可能没有
lorem
数据集,但韩语(
ko
)有。在这种情况下,
Bogus
会默认使用英文(
en
)的数据集。换句话说,如果找不到特定语言的数据集,就会退而使用英文的数据集。如果您有兴趣帮助贡献新的地区设置或更新现有的设置,请查看我们的创建地区设置页面获取更多信息。

来验证一下

    [Theory]
    [InlineData(null)]
    [InlineData("zh_CN")]
    public void Locales_ConfigTest(string? locale)
    {
        //default
        var faker = locale is null ? new Faker<Staff>() : new Faker<Staff>(locale);

        faker.RuleFor(u => u.Name, f => f.Person.FullName)
            .RuleFor(u => u.Email, f => f.Person.Email)
            .RuleFor(u => u.Age, f => f.Random.Number(18, 60))
            .RuleFor(u => u.Addresses, f => f.MakeLazy(f.Random.Number(1, 3), () => f.Address.FullAddress()).ToList())
            .RuleFor(u => u.Created, f => f.Date.PastOffset());
        var staff = faker.Generate();
        var consoleType = locale is null ? "default" : locale;
        testOutputHelperAccessor.Output?.WriteLine($"{consoleType}:{JsonConvert.SerializeObject(staff)}");
    }

OutPut

  default:{"Id":0,"Name":"Clyde Price","Email":"Clyde17@yahoo.com","Age":39,"Addresses":["46277 Abraham Parkways, South Spencerland, Guadeloupe","6470 Porter Island, Lesliehaven, Chad","10804 Halvorson Brook, Ninaton, Iran"],"Created":"2023-04-30T11:31:35.5106219+08:00"}
    zh_CN:{"Id":0,"Name":"昊焱 尹","Email":"_82@yahoo.com","Age":58,"Addresses":["孙桥5号, 珠林市, Costa Rica"],"Created":"2024-02-11T08:16:49.1807504+08:00"}

可以看出默认是
en
英文,通过设置
locale
可以实现国际化的输出。

生成相同数据集

// 如果您希望生成可重复的数据集,请设置随机数种子。
Randomizer.Seed = new Random(8675309);

这段代码用于设置随机数生成器的种子,以便生成可重复的数据集。通过指定一个固定的种子值,可以确保每次运行生成的随机数据都是相同的,从而实现数据集的重复性。

这个比较有意思,我们来做个
demo
,要求随机生成五个对象 要求下一次运行生成的还是同一批对象。

Bogus

Seed
就很容易实现。

   [Fact]
   public void Bogus_Compare_SeedTest()
   {
       // Arrange
       var faker = new Faker<Staff>()
           .RuleFor(u => u.Name, f => f.Person.FullName)
           .RuleFor(u => u.Email, f => f.Person.Email)
           .RuleFor(u => u.Age, f => f.Random.Number(18, 60))
           .RuleFor(u => u.Addresses, f => f.MakeLazy(f.Random.Number(1, 3), () => f.Address.FullAddress()).ToList())
           .RuleFor(u => u.Created, f => f.Date.PastOffset());

       // Act
       var staffs1 = Enumerable.Range(1, 5)
           .Select(_ => faker.UseSeed(_).Generate())
           .ToList();

       OutputStaffInformation(staffs1, "第一次");

       var staffs2 = Enumerable.Range(1, 5)
           .Select(_ => faker.UseSeed(_).Generate())
           .ToList();

       OutputStaffInformation(staffs2, "第二次");

       // Assert
       Assert.True(staffs1.All(staff1 => staffs2.Any(staff2 => staff1.Name == staff2.Name && staff1.Email == staff2.Email)));
   }

   private void OutputStaffInformation(List<Staff> staffs, string iteration)
   {
       foreach (Staff staff in staffs)
       {
           testOutputHelperAccessor.Output?.WriteLine($"{iteration}: name: {staff.Name}, email: {staff.Email}");
       }
   }
  • Arrange
    部分初始化了一个
    Faker<Staff>
    实例,并定义了一系列规则来生成
    Staff
    对象。
  • Act
    部分通过使用不同的种子值,生成了两组包含 5 个
    Staff
    对象的列表,并输出了每个
    Staff
    对象的姓名和邮箱信息。
  • Assert
    部分使用断言验证了两组生成的
    Staff
    列表中是否存在具有相同姓名和邮箱的对象,即通过 A
    ll 和 Any
    方法进行比较。

通过使用不同的种子值来生成多组数据,然后断言这些数据中是否存在相同的姓名和邮箱信息。

image

Bogus Api 支持

Bogus
之所以提供这么方便的假数据生成,得益于封装了开箱即用的获取各类数据的方法,如:

Address

  • ZipCode - 获取邮政编码。
  • City - 获取城市名称。
  • StreetAddress - 获取街道地址。
  • CityPrefix - 获取城市前缀。
  • CitySuffix - 获取城市后缀。
  • StreetName - 获取街道名称。
  • BuildingNumber - 获取建筑编号。
  • StreetSuffix - 获取街道后缀。
  • SecondaryAddress - 获取次要地址,如 '公寓 2' 或 '321 号套房'。
  • County - 获取县名。
  • Country - 获取国家。
  • FullAddress - 获取完整地址,包括街道、城市、国家。
  • CountryCode - 获取随机的 ISO 3166-1 国家代码。
  • State - 获取随机州名。
  • StateAbbr - 获取州名缩写。
  • Latitude - 获取纬度。
  • Longitude - 获取经度。
  • Direction - 生成基数或序数方向,例如:西北、南、西南、东。
  • CardinalDirection - 生成基数方向,例如:北、南、东、西。
  • OrdinalDirection - 生成序数方向,例如:西北、东南、西南、东北。

Commerce

  • Department - 获取随机商务部门。
  • Price - 获取随机产品价格。
  • Categories - 获取随机产品类别。
  • ProductName - 获取随机产品名称。
  • Color - 获取随机颜色。
  • Product - 获取随机产品。
  • ProductAdjective - 随机产品形容词。
  • ProductMaterial - 随机产品材料。
  • Ean8 - 获取随机的 EAN-8 条形码号码。
  • Ean13 - 获取随机的 EAN-13 条形码号码。

后面的可以查看官网
Api
官网地址在文末...

Bogus 库提供了丰富的 API 支持,涵盖了各种数据类型和用例,包括地址、商务、日期、金融、图片、互联网、Lorem 文本、姓名、电话等方面的虚假数据生成方法。

image

EFCore 利用 Bogus 生成种子数据

在我们的
Sample.Repository
中设置种子数据

  • 使用
    Bogus
    库生成虚假数据,填充到 Staffs 列表

public class FakeData
{
    public static List<Staff> Staffs = [];

    public static void Init(int count)
    {
        var id = 1;
        var faker = new Faker<Staff>()
            .RuleFor(_ => _.Id, f => id++)
       .RuleFor(u => u.Name, f => f.Person.FullName)
       .RuleFor(u => u.Email, f => f.Person.Email)
       .RuleFor(u => u.Age, f => f.Random.Number(18, 60))
       .RuleFor(u => u.Addresses, f => f.MakeLazy(f.Random.Number(1, 3), () => f.Address.FullAddress()).ToList())
       .RuleFor(u => u.Created, f => f.Date.PastOffset());
        var staffs = faker.Generate(count);
        FakeData.Staffs.AddRange(staffs);
    }
}
  • Program
    写入 1000 条种子数据

using (var context = app.Services.CreateScope().ServiceProvider.GetRequiredService<SampleDbContext>())
{
    context.Database.EnsureCreated();
    FakeData.Init(1000);
    await context.Staffs.AddRangeAsync(FakeData.Staffs);
    await context.SaveChangesAsync();
}

我这地方用的是
Microsoft.EntityFrameworkCore.InMemory
内存数据库,正常如果使用像
Sqlserver
,
MySQL

CodeFirst
模式可以在 DbContext 的
OnModelCreating
配置种子数据。

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        //FakeData.Init(1000);
        //builder.Entity<Staff>().HasData(FakeData.Staffs);
    }

来测试一下

通过我们
Sample.Api
提供的
GetAll
的方法测试一下种子数据

image

正好一千条测试数据,大功告成。

最后

在软件开发中,使用
Bogus
可以极大地简化测试数据的创建过程,同时结合
EFCore
的种子数据功能,可以快速生成并初始化数据库中的虚假数据。这种方法不仅提高了开发效率,还能确保测试数据的质量和一致性。通过本文的示例和说明,希望您能更加熟悉如何利用
Bogus

EFCore
来生成模拟数据和种子数据,从而为软件开发过程提供更好的支持和帮助,我们有大量数据的测试需求时,也不用再为创造数据而烦恼。


本文分享自华为云社区《
探究数据可视化:Bokeh vs. Altair
》,作者:柠檬味拥抱。

在数据科学和数据分析领域,数据可视化是一种强大的工具,可以帮助我们更好地理解数据、发现模式和趋势。Python作为一种流行的数据科学工具,拥有多种数据可视化库。本文将重点比较Bokeh和Altair这两个常用的Python数据可视化库,探讨它们的优缺点以及在不同场景下的适用性。

Bokeh 简介

Bokeh是一个交互式可视化库,它能够创建各种类型的交互式图表,包括散点图、线图、条形图等。Bokeh提供了丰富的工具,使用户能够在图表中进行缩放、平移和选择等操作。

Altair 简介

Altair是一个基于Vega和Vega-Lite的声明式统计可视化库。它的设计理念是简单性和一致性,使用者只需通过简单的Python语法即可创建复杂的可视化图表,而无需深入了解底层的绘图细节。

Bokeh 与 Altair 的比较

易用性:

  • Bokeh:相对而言,Bokeh的学习曲线较为陡峭,需要一定的时间来掌握其强大的交互功能和绘图选项。
  • Altair:Altair的语法相对简单直观,使用者可以更快速地创建出漂亮的图表,对于新手来说更易上手。

交互性:

  • Bokeh:Bokeh提供了丰富的交互工具,可以轻松地创建交互式图表,并且支持自定义交互行为。
  • Altair:虽然Altair的交互功能相对较少,但是它可以无缝地与其他交互库(如Panel)集成,实现更复杂的交互需求。

可视化表达能力:

  • Bokeh:Bokeh可以创建各种类型的图表,并且支持自定义图表的外观和布局。
  • Altair:Altair的语法设计简洁而灵活,可以轻松地实现复杂的可视化表达,例如使用facet进行分面绘图、使用layer进行图层叠加等。

示例代码和解析

Bokeh 示例:

frombokeh.plotting import figure, showfrombokeh.sampledata.iris import flowers

# 创建一个散点图
p
= figure(title="Iris Dataset", x_axis_label='Petal Length', y_axis_label='Petal Width')

# 添加散点数据
p.circle(flowers[
'petal_length'], flowers['petal_width'], legend_label='Iris Flowers', color='blue', size=8)

# 显示图表
show(p)

解析:

  • 使用Bokeh创建一个散点图,x轴为花瓣长度,y轴为花瓣宽度。
  • 使用Bokeh的
    circle
    方法添加散点数据,并指定图例标签、颜色和大小。
  • 最后调用
    show
    函数显示图表。

Altair 示例:

import altair asaltfromvega_datasets import data

# 加载数据集
iris
=data.iris()

# 创建散点图
scatter
=alt.Chart(iris).mark_circle().encode(
x
='petalLength:Q',
y
='petalWidth:Q',
color
='species:N',
tooltip
=['species', 'petalLength', 'petalWidth']
).properties(
title
='Iris Dataset',
width
=400,
height
=300).interactive()

# 显示图表
scatter

解析:

  • 使用Altair创建一个散点图,x轴为花瓣长度,y轴为花瓣宽度,颜色根据鸢尾花的种类进行编码。
  • 使用Altair的
    mark_circle
    方法创建散点图,并指定x、y、color等属性。
  • 最后通过
    .properties
    方法设置图表标题、宽度和高度,并调用
    .interactive()
    方法使图表具有交互功能。

通过以上示例和比较,我们可以看出,Bokeh和Altair都是功能强大的Python可视化库,它们各有优劣,选择合适的库取决于具体的需求和个人偏好。Bokeh适用于需要复杂交互的场景,而Altair则更适合于快速创建漂亮的可视化图表。

案例与代码示例

1. Bokeh 案例:

假设我们有一组销售数据,包括产品名称、销售量和销售额,我们想要使用 Bokeh 创建一个交互式条形图来展示各产品的销售情况。

frombokeh.plotting import figure, output_file, showfrombokeh.models import ColumnDataSource, HoverToolfrombokeh.transform import factor_cmap
import pandas
aspd

# 创建示例销售数据
sales_data
={'Product': ['Product A', 'Product B', 'Product C', 'Product D'],'Sales Volume': [100, 150, 200, 120],'Revenue': [5000, 7500, 10000, 6000]
}

df
=pd.DataFrame(sales_data)

# 设置输出文件
output_file(
"sales_bar_chart.html")

# 创建ColumnDataSource
source
=ColumnDataSource(df)

# 创建绘图对象
p
= figure(x_range=df['Product'], plot_height=350, title="Sales Summary",
toolbar_location
=None, tools="")

# 添加条形图
p.vbar(x
='Product', top='Sales Volume', width=0.9, source=source,
line_color
='white', fill_color=factor_cmap('Product', palette='Set1', factors=df['Product']))

# 添加悬停工具
p.add_tools(HoverTool(tooltips
=[("Product", "@Product"), ("Sales Volume", "@{Sales Volume}"), ("Revenue", "@Revenue")]))

# 设置图表属性
p.xgrid.grid_line_color
=None
p.y_range.start
= 0p.yaxis.axis_label= "Sales Volume"# 显示图表
show(p)

这段代码是用于创建一个简单的条形图来展示销售数据,并使用 Bokeh 库进行可视化。以下是代码的主要步骤解析:

导入必要的库:

  • from bokeh.plotting import figure, output_file, show
    : 从 Bokeh 库中导入创建绘图、输出文件和显示图表的函数。
  • from bokeh.models import ColumnDataSource, HoverTool
    : 从 Bokeh 库中导入用于处理数据源和悬停工具的相关类。
  • from bokeh.transform import factor_cmap
    : 从 Bokeh 库中导入用于颜色映射的转换函数。
  • import pandas as pd
    : 导入 Pandas 库,用于处理数据。
创建示例销售数据:

使用字典形式创建了示例的销售数据,包括产品名称、销售量和收入。

将数据转换为 Pandas DataFrame:

使用
pd.DataFrame()
函数将销售数据转换为 DataFrame。

设置输出文件:

使用
output_file()
函数设置输出文件名为 “sales_bar_chart.html”。

创建 ColumnDataSource:

使用
ColumnDataSource
类将 DataFrame 转换为 Bokeh 可用的数据源。

创建绘图对象:

使用
figure()
函数创建一个条形图对象
p
,指定了 x 轴的范围、绘图高度、标题等属性。

添加条形图:

使用
vbar()
方法向绘图对象添加条形图,指定了 x 值(产品名称)、条形的高度(销售量)、线条颜色、填充颜色等属性。

添加悬停工具:

使用
add_tools()
方法向绘图对象添加悬停工具,指定了悬停时显示的信息,包括产品名称、销售量和收入。

设置图表属性:

使用一系列属性设置函数设置图表的外观属性,如去除 x 轴的网格线、设置 y 轴起始值、设置 y 轴标签等。

显示图表:

使用
show()
函数显示绘图对象。

通过这些步骤,代码创建了一个包含销售数据的条形图,并通过悬停工具提供了额外的交互信息。

2. Altair 案例:

假设我们有一组学生的成绩数据,包括学生姓名、数学成绩和英语成绩,我们想要使用 Altair 创建一个散点图来展示学生的数学成绩与英语成绩的关系。

import altair asalt
import pandas
aspd

# 创建示例成绩数据
score_data
={'Student': ['Alice', 'Bob', 'Charlie', 'David', 'Emma'],'Math Score': [85, 90, 75, 80, 95],'English Score': [75, 85, 80, 70, 90]
}

df
=pd.DataFrame(score_data)

# 创建散点图
scatter_plot
=alt.Chart(df).mark_point().encode(
x
='Math Score',
y
='English Score',
tooltip
=['Student', 'Math Score', 'English Score']
).properties(
title
='Math vs English Scores',
width
=400,
height
=300).interactive()

# 显示图表
scatter_plot

这些示例代码展示了如何使用 Bokeh 和 Altair 分别创建交互式条形图和散点图,以展示销售数据和成绩数据的可视化。通过这些示例,可以更好地理解 Bokeh 和 Altair 在实际应用中的使用方法和效果。

3. Bokeh 案例(交互式地图):

假设我们有一组城市的经纬度数据,以及每个城市的人口数量,我们希望使用 Bokeh 创建一个交互式地图,显示每个城市的位置并以圆的大小表示人口数量。

frombokeh.plotting import figure, output_file, showfrombokeh.models import ColumnDataSource, HoverTool

# 示例城市数据
cities_data
={'City': ['New York', 'Los Angeles', 'Chicago', 'Houston'],'Latitude': [40.7128, 34.0522, 41.8781, 29.7604],'Longitude': [-74.0060, -118.2437, -87.6298, -95.3698],'Population': [8399000, 3990456, 2705994, 2320268]
}

df
=pd.DataFrame(cities_data)

# 设置输出文件
output_file(
"population_map.html")

# 创建ColumnDataSource
source
=ColumnDataSource(df)

# 创建绘图对象
p
= figure(plot_width=800, plot_height=600, title="Population Map",
toolbar_location
="below")

# 添加圆形标记
p.circle(x
='Longitude', y='Latitude', size='Population' / 100000,
fill_alpha
=0.6, line_color=None, source=source)

# 添加悬停工具
hover
=HoverTool()
hover.tooltips
= [("City", "@City"), ("Population", "@Population")]
p.add_tools(hover)

# 设置图表属性
p.xaxis.axis_label
= "Longitude"p.yaxis.axis_label= "Latitude"# 显示图表
show(p)

4. Altair 案例(堆叠柱状图):

假设我们有一组月度销售数据,包括销售额和利润,我们希望使用 Altair 创建一个堆叠柱状图,展示每个月的销售额和利润情况。

import altair asalt
import pandas
aspd

# 示例销售数据
sales_data
={'Month': ['Jan', 'Feb', 'Mar', 'Apr', 'May'],'Sales': [50000, 60000, 70000, 55000, 65000],'Profit': [20000, 25000, 30000, 22000, 27000]
}

df
=pd.DataFrame(sales_data)

# 创建堆叠柱状图
stacked_bar_chart
=alt.Chart(df).mark_bar().encode(
x
='Month',
y
='Sales',
color
=alt.value('blue'),
tooltip
=['Month', 'Sales']
).properties(
title
='Monthly Sales and Profit',
width
=400,
height
=300).interactive()+\
alt.Chart(df).mark_bar().encode(
x
='Month',
y
='Profit',
color
=alt.value('orange'),
tooltip
=['Month', 'Profit']
)

# 显示图表
stacked_bar_chart

这些示例代码展示了如何使用 Bokeh 和 Altair 分别创建交互式地图和堆叠柱状图,以展示城市人口分布和销售数据的可视化。这些示例为使用 Bokeh 和 Altair 进行数据可视化提供了更多的灵感和实践经验。

总结

本文对Python中两个常用的数据可视化库 Bokeh 和 Altair 进行了比较和探讨。通过对它们的特点、优缺点以及使用示例的详细分析,读者可以更好地了解这两个库的功能和适用场景,从而更好地选择合适的库来进行数据可视化工作。

在比较中,我们发现:

  • Bokeh 提供了丰富的交互功能和自定义选项,适用于需要复杂交互和自定义图表外观的场景,但学习曲线较陡。
  • Altair 的语法简洁直观,易于上手,适用于快速创建漂亮的可视化图表,但交互功能相对较少。

针对不同的需求和技能水平,读者可以灵活选择使用 Bokeh 或 Altair 进行数据可视化。Bokeh 适用于需要复杂交互和自定义外观的场景,而 Altair 则更适合快速创建漂亮的可视化图表。

通过本文的介绍和示例代码,读者可以进一步掌握 Bokeh 和 Altair 的使用方法,并在实践中运用它们来进行数据可视化工作。同时,我们也展望了数据可视化领域未来的发展趋势,包括增强交互性、提升性能和效率、整合机器学习和深度学习等方面。

总之,数据可视化作为数据科学和数据分析领域的重要工具,将在未来继续发挥重要作用。Bokeh 和 Altair 等可视化库的不断发展和完善,将为用户提供更加强大和便捷的数据可视化工具,助力数据分析和决策支持工作的开展。

点击关注,第一时间了解华为云新鲜技术~

前言

对于大部分程序员来说,主要的工作时间是在开发和修复BUG。

有可能修改了一个BUG,会导致几个新BUG的产生,不断循环。

那么,有没有办法能够减少BUG,保证代码质量,提升工作效率?

答案是肯定的。

如果能做到,我们多出来的时间,多摸点鱼,做点自己喜欢的事情,不香吗?

这篇文章跟大家一起聊聊减少代码BUG的10个小技巧,希望对你会有所帮助。

1 找个好用的开发工具

在日常工作中,找一款好用的开发工具,对于开发人员来说非常重要。

不光可以提升开发效率,更重要的是它可以帮助我们减少BUG。

有些好的开发工具,比如:
idea
中,对于包没有引入,会在相关的类上面
标红

并且idea还有
自动补全
的功能,可以有效减少我们在日常开发的过程中,有些单词手动输入的时候敲错的情况发生。

2 引入Findbugs插件

Findbugs是一款Java静态代码分析工具,它专注于寻找真正的缺陷或者潜在的性能问题,它可以帮助java工程师提高代码质量以及排除隐含的缺陷。

Findbugs运用Apache BCEL 库分析类文件,而不是源代码,将字节码与一组缺陷模式进行对比以发现可能的问题。

可以直接在idea中安装FindBugs插件:

之后可以选择分析哪些代码:

分析结果:

点击对应的问题项,可以找到具体的代码行,进行修复。

Findbugs的检测器已增至300多条,被分为不同的类型,常见的类型如下:

  • Correctness:这种归类下的问题在某种情况下会导致bug,比如错误的强制类型转换等。

  • Bad practice:这种类别下的代码违反了公认的最佳实践标准,比如某个类实现了equals方法但未实现hashCode方法等。

  • Multithreaded correctness:关注于同步和多线程问题。

  • Performance:潜在的性能问题。

  • Security:安全相关。

  • Dodgy:Findbugs团队认为该类型下的问题代码导致bug的可能性很高。

3 引入CheckStyle插件

CheckStyle作为检验代码规范的插件,除了可以使用配置默认给定的开发规范,如Sun、Google的开发规范之外,还可以使用像阿里的开发规范的插件。

目前国内用的比较多的是阿里的代码开发规范,我们可以直接通过idea下载插件:

如果想检测某个文件:

可以看到结果:

阿里巴巴规约扫描包括:

  1. OOP规约
  2. 并发处理
  3. 控制语句
  4. 命名规约
  5. 常量定义
  6. 注释规范

Alibaba Java Coding Guidelines 专注于Java代码规范,目的是让开发者更加方便、快速规范代码格式。

该插件在扫描代码后,将不符合规约的代码按 Blocker、Critical、Major 三个等级显示出来,并且大部分可以自动修复。

它还基于Inspection机制提供了实时检测功能,编写代码的同时也能快速发现问题。

最近就业形式比较困难,为了感谢各位小伙伴对苏三一直以来的支持,我特地创建了一些工作内推群, 看看能不能帮助到大家。
你可以在群里发布招聘信息,也可以内推工作,也可以在群里投递简历找工作,也可以在群里交流面试或者工作的话题。

进群方式,添加苏三的私人微信:su_san_java,备注:博客园+所在城市,即可加入。

4 用SonarQube扫描代码

SonarQube是一种自动代码审查工具,用于检测代码中的错误,漏洞和代码格式上的问题。

它可以与用户现有的工作流程集成,以实现跨项目分支和提取请求的连续代码检查,同时也提供了可视化的管理页面,用于查看检测出的结果。

SonarQube通过配置的代码分析规则,从可靠性、安全性、可维护性、覆盖率、重复率等方面分析项目,风险等级从A~E划分为5个等级;

同时,SonarQube可以集成pmd、findbugs、checkstyle等插件来扩展使用其他规则来检验代码质量。

一般推荐它跟Jenkins集成,做成每天定时扫描项目中test分支中的代码问题。

5 用Fortify扫描代码

Fortify 是一款广泛使用的静态应用程序安全测试(SAST)工具。

它具有代码扫描、漏斗扫描和渗透测试等功能。它的设计目的是有效地检测和定位源代码中的漏洞。

它能帮助开发人员识别和修复代码中的安全漏洞。

Fortify的主要功能:

  • 静态代码分析:它会对源代码进行静态分析,找出可能导致安全漏洞的代码片段。它能识别多种类型的安全漏洞,如 SQL 注入、跨站脚本(XSS)、缓冲区溢出等。

  • 数据流分析:它不仅分析单个代码文件,还跟踪应用程序的数据流。这有助于找到更复杂的漏洞,如未经验证的用户输入在应用程序中的传播路径。

  • 漏洞修复建议:发现潜在的安全漏洞时,它会为开发人员提供修复建议。

  • 集成支持:它可以与多种持续集成(CI)工具(如 Jenkins)和应用生命周期管理(ALM)工具(如 Jira)集成,实现自动化的代码扫描和漏洞跟踪。

  • 报告和度量:它提供了丰富的报告功能,帮助团队了解项目的安全状况和漏洞趋势。

使用Fortify扫描代码的结果:

一般推荐它跟Jenkins集成,定期扫描项目中test分支中的代码安全问题。

6 写单元测试

有些小伙伴可能会问:写单元测试可以减少代码的BUG?

答案是肯定的。

我之前有同事,使用的测试驱动开发模式,开发一个功能模块之前,先把单元测试写好,然后再真正的开发业务代码。

后面发现他写的代码速度很快,而且代码质量很高,是一个开发牛人。

如果你后期要做系统的代码重构,你只是重写了相关的业务代码,但业务逻辑并没有修改。

这时,因为有了之前写好的单位测试,你会发现测试起来非常方便。

可以帮你减少很多BUG。

7 功能自测

功能自测,是程序员的基本要求。

但有些程序员自测之后,BUG还是比较多,而有些程序员自测之后,BUG非常少,这是什么原因呢?

可能有些人比较粗心,有些人比较细心。

其实更重要的是测试的策略。

有些人喜欢把所有相关的功能都开发完,然后一起测试。

这种情况下,相当于一个黑盒测试,需要花费大量的时间,梳理业务逻辑才能测试完整,大部分情况下,开发人员是没法测试完整的,可能会有很多bug测试不出来。

这种做法是没有经过单元测试,直接进行了集成测试。

看似节省了很多单元测试的时间,但其实后面修复BUG的时间可能会花费更多。

比较推荐的自测方式是:一步一个脚印。

比如:你写了一个工具类的一个方法,就测试一下。如果这个方法中,调用了另外一个关键方法,我们可以先测试一下这个关键方法。

这样可以写出BUG更少的代码。

最近就业形式比较困难,为了感谢各位小伙伴对苏三一直以来的支持,我特地创建了一些工作内推群, 看看能不能帮助到大家。
你可以在群里发布招聘信息,也可以内推工作,也可以在群里投递简历找工作,也可以在群里交流面试或者工作的话题。

进群方式,添加苏三的私人微信:su_san_java,备注:博客园+所在城市,即可加入。

8 自动化测试

有些公司引入了自动化测试的功能。

每天都会自动测试,保证系统的核心流程没有问题。

因为我们的日常开发中,经常需要调整核心流程的代码。

不可能每调整一次,都需要把所有的核心流程都测试一遍吧,这样会浪费大量的时间,而且也容易遗漏一些细节。

如果引入了自动化测试的功能,可以帮助我们把核心流程都测试一下。

避免代码重构,或者修改核心流程,测试时间不够,或者测试不完全的尴尬。

自动化测试,可以有效的减少核心流程调整,或者代码重构中的BUG。

9 代码review

很多公司都有代码review机制。

我之前也参与多次代码review的会议,发现代码review确实可以找出很多BUG。

比如:一些代码的逻辑错误,语法的问题,不规范的命名等。

这样问题通过组内的代码review一般可以检查出来。

有些国外的大厂,采用
结对编程
的模式。

同一个组的两个人A和B一起开发,开发完之后,A reivew B的代码,同时B review A的代码。

因为同组的A和B对项目比较熟,对对方开发的功能更有了解,可以快速找出对外代码中的一些问题。

能够有效减少一些BUG。

10 多看别人的踩坑分享

如果你想减少日常工作中的代码BUG,或者线上事故,少犯错,少踩坑。

经常看别人真实的踩坑分享,是一个非常不错的选择,可以学到一些别人的工作经验,帮助你少走很多弯路。

网上有许多博主写过自己的踩坑记录,大家可以上网搜一下。

也可以看看我自己总结的《
程序员最常见的100个问题
》,里面有非常详细的记录,干货很多,还是非常值得一看的。

最后说一句,本文总结了10种减少代码BUG的小技巧,但我们要根据实际情况选择使用,并非所有的场景都适合。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注苏三的公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。