分类 技术 下的文章

Linux 有很多用于查看图像的 GUI 应用。但我从来没有尝试过用任何命令行应用来查看它。

幸运的是,在使用 ImageMagick 工具时,我得到了一个从终端查看图像的命令。命令名是 “display,它是 ImageMagick 工具的一部分。这是一个很好的工具,允许类 UNIX 用户从终端查看图像。

此外,我还为此用途得到了另一个很好的工具,叫做 FIM。

我们将向你展示如何安装并使用它从 Linux 终端查看图像。这些命令使用系统的 帧缓冲 framebuffer 直接从命令行显示图像。

如何使用 display 命令从终端查看图像

ImageMagick 是一个自由开源、功能丰富、基于命令行的图像处理工具。它用于创建、编辑、合成或转换位图图像。它可以读取和写入各种格式(超过 200 种)的图像,包括 PNG、JPEG、GIF、PDF、SVG 等。它可以调整图像的大小、镜像、旋转、转换图像、调整图像颜色、应用各种特殊效果等。它支持批处理,允许你一次处理所有图像。

如何安装 ImageMagick?

ImageMagick 软件包包含在大多数 Linux 发行版的官方仓库中。使用发行版软件包管理器来安装它。

**需要注意的是:**确保你的 Linux 系统上已经安装了 “Development Tools” 包,这是安装的前提条件。

对于 RHEL/CentOS 6/7 系统,请使用 yum 命令 安装 ImageMagick:

$ sudo yum install -y ImageMagick ImageMagick-devel

在 RHEL/CentOS 8 和 Fedora 系统,使用 dnf 命令 安装 ImageMagick:

$ sudo dnf install -y ImageMagick ImageMagick-devel

对于 Debian/Ubuntu 系统,使用 apt 命令apt-get 命令 安装 ImageMagick:

$ sudo apt-get update
$ sudo apt-get install imagemagick

对于 openSUSE 系统,使用 zypper 命令 安装 ImageMagick:

$ sudo zypper install -y ImageMagick

要查看任何图像文件,请运行 display 命令,如下所示。你可以按下 Esc/q 按钮关闭图像:

$ display bird.jpg

如果你想用指定的窗口大小打开图像,请使用 -geometry 标志:

$ display -geometry 1000x600 ~/Downloads/bird.jpg

你也可以通过 display 命令输入图像的位置信息。下面的命令可以从桌面的距顶部 800 像素和据左上角 800 像素处打开图像:

$ display -geometry 1000x600+800+800 ~/Downloads/bird.jpg

如果你想用 display 命令调整图像的大小,请使用以下格式:

$ display -resize 600x400 ~/Downloads/bird.jp

另外,你也可以使用百分比来调整图片的大小:

$ display -resize 50% ~/Downloads/bird.jpg

如何使用 fim 命令从终端查看图像

FIM 是一个专门为 Linux 设计的轻量级全局图像查看器。但它并不局限于 Linux,它也可配置在其他操作系统上运行,如 MS-Windows。

对于熟悉 VIM 文本编辑器等软件的用户来说,它是高度可定制和可脚本化的图像查看器。它可以全屏显示图像,并且可以使用键盘快捷键轻松控制。它是一款非常轻量级的工具,因为它只依赖于某些库。

它可以打开许多文件格式,它可以在以下视频模式下显示图像:

  • 使用 Linux 帧缓冲设备图形化。
  • 在 X/Xorg 下,使用 SDL 库图形化
  • 在 X/Xorg 下,使用 Imlib2 库图形化。
  • 使用 AAlib 库,在任意文本控制台中以 ASCII 艺术形式呈现。

运行时自动检测或选择正确的视频模式,如果需要,可以在构建前配置时选择加入或去除。

FIM 是 “Fbi IMproved” 的缩写,是 Fbi Image Viewer 的复刻版本。

FIM 可以很容易地安装在基于 Debian/Ubuntu 的系统上,因为该软件包在发行版的官方仓库中是可用的。对于其他发行版,你可能需要从源码编译它:

$ sudo apt install fim

安装完毕后,你可以使用以下命令显示图像:

$ fim bird.jpg

你可以使用 -a 选项自动缩放图像:

$ fim -a bird.jpg

如果你要打开当前目录中的多个图像文件,请使用通配符将它们全部打开。使用 PageUp/PageDown 键盘快捷键移动到下一张或上一张图像:

$ fim -a *.jpg

要以 ASCII 格式查看图像,可以使用 -t 标志:

$ fim -t bird.jpg

下面的键盘快捷键可以让你控制图像:

  • PageUp/PageDown:上一张/下一张图片。
  • +/-:放大/缩小
  • a:自动缩放
  • w:适应宽度
  • ESC/q:退出

via: https://www.2daygeek.com/how-to-view-display-images-from-linux-terminal/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Vi 是典型的 Unix 文本编辑器。来了解一下它或它的各种化身:Vim、Neovim、gVim、nvi 或 Elvis,它适用于 Linux、macOS、Windows 或 BSD。

不管你用的是 Vim、Neovim、gVim、nvi,甚至是 Elvis,其实都是这个典型的 Unix 编辑器 Vi。可能每一个 Linux 和 BSD 发行版中都包含了 Vi,Vi 是一个轻量级的简约型文本编辑器,由于其简单简洁的键盘快捷键和双模式设计,很多用户都喜欢它。

最初的 Vi 编辑器是由 C shell 的创建者 Bill Joy 编写的应用程序。现代 Vi 的化身已经增加了许多功能,包括多级撤销、插入模式下更好的导航、行折叠、语法高亮、插件支持等等。Vim 被认为是它的最流行的现代实现,大多数人在提到 Vi 时实际上是指 Vim。

所有这些化身都是为了同一个目标,所以本文将从通用的场景来探讨 Vi。你的计算机上的版本可能略有不同,但你仍然可以从 Vi 编辑文本的方式中获益。

安装 Vi

如果你运行的是 Linux、macOS 或 BSD,那么你已经安装了 vi 命令。如果你在 Windows 上,你可以下载 Vim 和 gVim

 title=

NetBSD上,nvi 是 Vi 的常见替代品,而 Slackware 则提供了 Elvis(和 Vim),流行的 Neovim 复刻旨在帮助用户用 Lua 扩展 Vim。

启动 Vi

在终端中用 vi 命令启动 Vi 或 Vim。如果在你的系统中没有找到 .vimrc 文件,那么 Vim 就会以 Vi 兼容模式启动(也可以用 -C 选项强制启动该模式)。如果你想使用 gVim 以拥有一个图形用户界面(GUI),你可以从桌面的应用程序菜单中启动它。

如果你是一个刚刚学习 Vi 的新用户,使用图形用户界面是一个很好的方法,可以在你可能期望的文本编辑器的行为和 Vi 的设计行为之间提供一个缓冲带。图形用户界面版本有一个菜单栏,一些鼠标集成,一个工具栏和其他功能,这可以帮助你找到你可能认为在典型的文本编辑器中理所当然的基本功能,但还不知道如何在 Vi 中做。

如何使用 Vi

学习 Vi 最简单的方法可能是使用 vimtutor,这是一个与 Vim 打包在一起的交互式教程。要开始学习该教程,启动 vimtutor 并阅读说明,尝试每个练习。正如教程中所说,学好 Vi 不是记住什么键做什么,而是建立肌肉记忆,以在输入时调用常用的动作。

Esc 键

学习 Vi 的第一件重要的事就是掌握 Esc 键。Esc 是激活命令模式的工具,很快你就会明白,在 Vi 中,只要你不确定,就按 Esc。在命令模式下,你按下的任何键都不会被输入到你正在处理的文本文档中,而是被 Vi 解释为一条命令。例如,要将光标向左移动,你可以按键盘上的 H 键。如果你处于插入模式,那么按 H 键就会输入字母 H,就像你期望的那样。但在命令模式下,按 H 向左移动,L 向右移动,J 向下移动,K 向上移动。

命令模式和插入模式的分离与其他文本编辑器的工作方式形成了鲜明的对比,由于这种设计,这可能是 Vi 最显著的差异化。不过有趣的是,理论上来说,它与你可能已有的工作方式并没有太大的区别。毕竟,当你把手从键盘上拿开,用鼠标选择文本时,你基本上是将自己置于一种命令模式中。在 Vi 中,你不需要把手从键盘上移开来移动鼠标,也不需要按功能键或 Ctrl 键,而是将编辑器放入一种特殊的操作模式中,使你的按键重新分配到命令上,而不是文字输入。

扩展 Vi

在 Vim 8.0 版本之前,Vi 在很大程度上“只是”一个文本编辑器。它有插件,但安装插件是一个手动的过程,很多用户从未想过要这么做。幸运的是,Vim 8 及以上版本提供了对插件管理的支持,使得安装和加载插件变得轻而易举。

安装 Vim 的插件可以通过 vim-plug 功能来完成。例如,要安装 Vi 文件浏览器 NERDTree

:PlugInstall NERDTree

你也可以更新插件:

:PlugUpdate NERDTree

关于使用 vim-plug 和手动安装插件和主题的更多信息,请阅读我的文章《如何安装 Vim 插件》。

默认 Vi

Vi 不仅仅流行,它还是一个 POSIX 标准。它是每个系统管理员都应该知道如何使用的应用程序,即使他们不打算每天使用它。它也是一个快速而简单的编辑器,所以一旦你熟练掌握了它,它可能就是你一直在寻找的编辑器。


via: https://opensource.com/article/20/12/vi-text-editor

作者:Seth Kenlon 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

你是如何在 Linux 上使用 C 编写你的程序的?它确实是非常简单的,由三个简单的步骤组成。

步骤 1: 编写你的 C 程序,并使用一个 .c 的扩展名进行保存。例如,my_program.c

步骤 2: 在一个终端中使用 gcc 编译器来编译程序并生成目标文件,像这样:

gcc -o my_program my_program.c

步骤 3: 在 Linux 中,你可以以运行生成的对象文件的方式来运行你的 C 程序:

./my_program

这只是如何在 Linux 中编译和运行 C 程序的简要总结。假设你是 C 语言或 Linux 系统的新手,我将仔细演示这些步骤,以便你能在 Linux 环境中舒服地编写 C 程序。

事实上,我将讨论如何在 Linux 终端中以及在代码编辑器中运行 C 程序的两种方式。

方法 1: 在 Linux 终端中运行 C 程序

为了在 Linux 中运行一个 C 程序,你需要在你的系统上有一个 C 编译器。最流行的编译器是 gcc GNU 编译器套件 GNU Compiler Collection )。

你可以使用你发行版的软件包管理器来安装 gcc 。在基于 Debian 和 Ubuntu 的 Linux 发行版中,使用 apt 命令:

sudo apt install gcc

切换到保存你的 C 程序的目录(或者提供路径),然后通过编译程序生成对象文件:

gcc -o my_program my_program.c

记住,提供输出对象文件(-o my_program)是可选的。如果你不提供,那么将自动生成一个名称为 a.out 的对象文件。但是这样并不好,因为编译每个 C 程序都会覆盖它,而且你也不知道这个 a.out 对象文件究竟属于哪个程序。

在你的对象文件生成后,运行它来运行 C 程序。它已经能够执行了。像这样简单地使用它:

./my_program

接下来,如果你的程序是正确的,它将显示出你所期望的输出。正如你所看到的,这和 在 Linux 中运行 C++ 程序 没什么不同。

每更改一次你的程序,你都必须先重新编译它,然后再次运行生成的对象文件来运行这个新的 C 程序。

方法 2: 如何在 Linux 中使用一个诸如 VSCode 之类的代码编辑器来运行 C 程序

并不是每一个人都能适应命令行和终端,我完全理解这一点。

你可以使用一个诸如 Eclipse 或 Code Blocks 之类的真正的 C/C++ IDE ,但是它们是很重量级的程序,通常更适合于大型的项目。

我建议使用一个开源的代码编辑器,像 VSCode 或 Atom 。它们基本上是文本编辑器,但是你可以通过安装附加组件来直接在图形化的代码编辑器中编译和运行程序。

在这个示例中,我使用 VSCode 编辑器。它是一个来自微软的 非常流行的开源的代码编辑器

首先,在 Ubuntu 的 软件中心中安装 VSCode 。对于其它发行版来说,请检查你的 Linux 发行版的软件包管理器或软件中心。你可以参看它的官方网站来查看更多的信息。

启动 VSCode ,打开或创建一个工程,在这里创建你的 C 程序。我使用一个简单的 Hello World 程序作为示例。

你必须确保你已经在你的 Linux 系统上安装了 gcc 编译器。

sudo apt install gcc

接下来你要做的事是使用一个允许你运行 C 代码的扩展。微软可能会提示你安装它的 C/C++ 程序扩展,但它的设置很复杂,因此我不推荐。

相反,我建议你使用 Code Runner 扩展。它是一个简单直接的扩展,你可以在不使用额外配置的情况下轻松地运行 C 和 C++ 代码。

转到扩展标签页,在其中搜索和安装 “Code Runner” 。

安装 Code Runner 扩展来运行 C/C++ 程序

重新启动 VSCode 。现在,你能够使用下面方法中的其中一个来运行 C 代码:

  • 使用快捷键 Ctrl+Alt+N
  • 按下 F1 ,接下来选择或输入 “Run Code” 。
  • 在文本编辑器中右键单击,从上下文菜单中单击 “Run code” 。

右键单击程序文件,然后选择 Run Code

当你运行这个 C 程序时,它将会被自动编译和运行。你可以在编辑器底部打开的终端中看到输出。还有比这更好的事情吗?

程序输出显示在编辑器的底部

你更喜欢哪一种方法?

在 Linux 命令行中运行一些 C 程序是没有问题的,但是使用一个代码编辑器会更容易一些,而且会节省时间。你不觉得吗?

你可以自己决定想使用哪一种方法。


via: https://itsfoss.com/run-c-program-linux/

作者:Abhishek Prakash 选题:lujun9972 译者:robsean 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

有时需要在系统中添加另一块磁盘。这就是 逻辑卷管理 Logical Volume Management (LVM)的用武之地。LVM 的好处之处在于它相当灵活。有几种方法可以添加一块磁盘。这篇文章介绍了一种方法。

注意!

这篇文章并不包括将新的磁盘物理地安装到系统中的过程。请查阅你的系统和磁盘文档,了解如何正确地进行安装。

重要: 一定要确保你已经备份重要数据。如果新磁盘已有数据,那么本文中描述的步骤将破坏数据。

最好了解

本文并没有深入介绍 LVM 的每一个功能,重点是添加磁盘。但基本上你要了解,LVM 有 卷组 volume group (VG),它由一个或多个分区和/或磁盘组成。你把这些分区或磁盘以 物理卷 physical volume (PV)的方式添加到卷组。一个卷组可以分成许多 逻辑卷 logical volume (LV)。逻辑卷可以作为文件系统、ramdisk 等其他存储使用。更多信息可以在这里)中找到。

可以看作是,把物理卷形成一个存储池(一个卷组),然后从这个存储池中划分出逻辑卷,供你的系统直接使用。

准备

确保你能看到你要添加的磁盘。在添加磁盘之前使用 lsblk 查看哪些存储空间已经可用或正在使用。

$ lsblk
NAME                   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
zram0                  251:0    0  989M  0 disk [SWAP]
vda                    252:0    0   20G  0 disk
├─vda1                 252:1    0    1G  0 part /boot
└─vda2                 252:2    0   19G  0 part
└─fedora_fedora-root   253:0    0   19G  0 lvm  /

本文使用的是带有虚拟存储的虚拟机,因此设备名称以 vda 开头代表第一个磁盘,vdb 代表第二个磁盘,以此类推。你的设备名称可能不同。许多系统会将 sda 作为第一个物理磁盘,sdb 代表第二个磁盘,以此类推。

当已连接新磁盘,并且你的系统已备份且正在运行,再次使用 lsblk 来查看新的块设备。

$ lsblk
NAME                   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
zram0                  251:0    0  989M  0 disk [SWAP]
vda                    252:0    0   20G  0 disk
├─vda1                 252:1    0    1G  0 part /boot
└─vda2                 252:2    0   19G  0 part
└─fedora_fedora-root 253:0    0   19G  0 lvm  /
vdb                    252:16   0   10G  0 disk

现在有一个名为 vdb 的新设备。该设备的位置是 /dev/vdb

$ ls -l /dev/vdb
brw-rw----. 1 root disk 252, 16 Nov 24 12:56 /dev/vdb

我们可以看到磁盘,但我们还不能用 LVM 来使用它。如果你运行 blkid,你应该不会看到它被列出。对于这个和之后的命令,你需要确保你的系统已配置好,这样你可以使用 sudo

$ sudo blkid
/dev/vda1: UUID="4847cb4d-6666-47e3-9e3b-12d83b2d2448" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="830679b8-01"
/dev/vda2: UUID="k5eWpP-6MXw-foh5-Vbgg-JMZ1-VEf9-ARaGNd" TYPE="LVM2_member" PARTUUID="830679b8-02"
/dev/mapper/fedora_fedora-root: UUID="f8ab802f-8c5f-4766-af33-90e78573f3cc" BLOCK_SIZE="4096" TYPE="ext4"
/dev/zram0: UUID="fc6d7a48-2bd5-4066-9bcf-f062b61f6a60" TYPE="swap"

将磁盘添加到 LVM 中

使用 pvcreate 初始化磁盘。你需要传递设备的完整路径。在这个例子中,它是 /dev/vdb。在你的系统中,它可能是 /dev/sdb 或其他设备名。

$ sudo pvcreate /dev/vdb
Physical volume "/dev/vdb" successfully created.

当你运行 blkid 时,你应该看到磁盘已经被初始化为一个 LVM2_member

$ sudo blkid
/dev/vda1: UUID="4847cb4d-6666-47e3-9e3b-12d83b2d2448" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="830679b8-01"
/dev/vda2: UUID="k5eWpP-6MXw-foh5-Vbgg-JMZ1-VEf9-ARaGNd" TYPE="LVM2_member" PARTUUID="830679b8-02"
/dev/mapper/fedora_fedora-root: UUID="f8ab802f-8c5f-4766-af33-90e78573f3cc" BLOCK_SIZE="4096" TYPE="ext4"
/dev/zram0: UUID="fc6d7a48-2bd5-4066-9bcf-f062b61f6a60" TYPE="swap"
/dev/vdb: UUID="4uUUuI-lMQY-WyS5-lo0W-lqjW-Qvqw-RqeroE" TYPE="LVM2_member"

你可以使用 pvs 列出当前所有可用的物理卷:

$ sudo pvs
PV         VG            Fmt  Attr PSize   PFree
/dev/vda2  fedora_fedora lvm2 a--  <19.00g     0
/dev/vdb                 lvm2 ---   10.00g 10.00g

/dev/vdb 被列为一个 PV (物理卷),但还没有分配到一个 VG (卷组)。

将物理卷添加到一个卷组

你可以使用 vgs 找到可用的卷组列表:

$ sudo vgs
VG            #PV #LV #SN Attr   VSize   VFree
fedora_fedora   1   1   0 wz--n- 19.00g    0

在本例中,只有一个卷组可用。接下来,将物理卷添加到 fedora_fedora

$ sudo vgextend fedora_fedora /dev/vdb
Volume group "fedora_fedora" successfully extended

你现在应该看到物理卷已被添加到卷组中:

$ sudo pvs 
PV        VG            Fmt  Attr PSize   PFree
/dev/vda2 fedora_fedora lvm2 a–   <19.00g 0
/dev/vdb  fedora_fedora lvm2 a–   <10.00g <10.00g

看一下卷组:

$ sudo vgs
VG            #PV #LV #SN Attr  VSize  VFree
fedora_fedora 2   1   0   wz–n- 28.99g <10.00g

你也可以获得具体卷组和物理卷的详细列表:

$ sudo vgdisplay fedora_fedora
--- Volume group ---
VG Name               fedora_fedora
System ID
Format                lvm2
Metadata Areas        2
Metadata Sequence No  3
VG Access             read/write
VG Status             resizable
MAX LV                0
Cur LV                1
Open LV               1
Max PV                0
Cur PV                2
Act PV                2
VG Size               28.99 GiB
PE Size               4.00 MiB
Total PE              7422
Alloc PE / Size       4863 / 19.00 GiB
Free  PE / Size       2559 / 10.00 GiB
VG UUID               C5dL2s-dirA-SQ15-TfQU-T3yt-l83E-oI6pkp

看下物理卷:

$ sudo pvdisplay /dev/vdb
--- Physical volume ---
PV Name               /dev/vdb
VG Name               fedora_fedora
PV Size               10.00 GiB / not usable 4.00 MiB
Allocatable           yes
PE Size               4.00 MiB
Total PE              2559
Free PE               2559
Allocated PE          0
PV UUID               4uUUuI-lMQY-WyS5-lo0W-lqjW-Qvqw-RqeroE 

现在我们已经添加了磁盘,我们可以为逻辑卷 (LV) 分配空间:

$ sudo lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
root fedora_fedora -wi-ao---- 19.00g

看一下逻辑卷。下面是详细的逻辑卷信息:

$ sudo lvdisplay fedora_fedora/root
--- Logical volume ---
LV Path                /dev/fedora_fedora/root
LV Name                root
VG Name                fedora_fedora
LV UUID                yqc9cw-AvOw-G1Ni-bCT3-3HAa-qnw3-qUSHGM
LV Write Access        read/write
LV Creation host, time fedora, 2020-11-24 11:44:36 -0500
LV Status              available
LV Size                19.00 GiB
Current LE             4863
Segments               1
Allocation             inherit
Read ahead sectors     auto
- currently set to     256
Block device           253:0

查看根文件系统(/)的大小,并将它与逻辑卷大小进行比较。

$ df -h /
Filesystem                      Size  Used Avail Use% Mounted on
/dev/mapper/fedora_fedora-root   19G  1.4G   17G   8% /

逻辑卷和文件系统大小都为 19G。让我们给根逻辑卷(root)增加 5G。

$ sudo lvresize -L +5G fedora_fedora/root
Size of logical volume fedora_fedora/root changed from 19.00 GiB (4863 extents) to 24.00 GiB (6143 extents).
Logical volume fedora_fedora/root successfully resized.

我们现在有 24G 的逻辑卷可用。看看根文件系统(/)。

$ df -h /
Filesystem                      Size  Used Avail Use% Mounted on
/dev/mapper/fedora_fedora-root   19G  1.4G   17G   8% /

我们仍然显示只有 19G 的空闲空间,这是因为逻辑卷与文件系统不一样。要使用增加到逻辑卷的新空间,请调整文件系统的大小。

$ sudo resize2fs /dev/fedora_fedora/root
resize2fs 1.45.6 (20-Mar-2020)
Filesystem at /dev/fedora_fedora/root is mounted on /; on-line resizing required
old_desc_blocks = 3, new_desc_blocks = 3
The filesystem on /dev/fedora_fedora/root is now 6290432 (4k) blocks long.

看看文件系统的大小。

$ df -h /
Filesystem                      Size  Used Avail Use% Mounted on
/dev/mapper/fedora_fedora-root   24G  1.4G   21G   7% /

正如你所看到的,根文件系统(/)已经占用了逻辑卷上的所有可用空间,而且不需要重新启动。

现在你已经将一个磁盘初始化为物理卷,并使用新的物理卷扩展了卷组。之后,你增加了逻辑卷的大小,并调整了文件系统的大小,以使用逻辑卷的新空间。


via: https://fedoramagazine.org/add-storage-to-your-fedora-system-with-lvm/

作者:Tim Bosse 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用这些开源框架创建一个彩色地图,显示病毒的可能的传播路径。

对于一个全球旅行司空见惯的世界来说,疾病的传播是一个真正令人担忧的问题。一些组织会跟踪重大的流行病(还有所有普遍的流行病),并将他们的跟踪工作获得的数据公开出来。不过,这些原始的数据对人来说可能很难处理,这就是为什么数据科学如此重要的原因。比如,用 Python 和 Pandas 可视化 COVID-19 在全球范围内的传播路径可能对这些数据的分析有所帮助。

最开始,当面对如此大数量的原始数据时可能难以下手。但当你开始处理数据之后,慢慢地就会发现一些处理数据的方式。下面是用于处理 COVID-19 数据的一些常见的情况:

  1. 从 GitHub 上下载 COVID-19 的国家每日传播数据,保存为一个 Pandas 中的 DataFrame 对象。这时你需要使用 Python 中的 Pandas 库。
  2. 处理并清理下载好的数据,使其满足可视化数据的输入格式。所下载的数据的情况很好(数据规整)。这个数据有一个问题是它用国家的名字来标识国家,但最好是使用三位数的 ISO 3 码(国家代码表)来标识国家。为了生成 ISO 3 码,可是使用 pycountry 这个 Python 库。生成了这些代码之后,可以在原有的 DataFrame 上增加一列,然后用这些代码填充进去。
  3. 最后为了实现可视化,使用 Plotly 库中的 express 模块。这篇文章是使用名为choropleth 的地图(可在 Plotly 库中获得)来可视化该疾病在全球的传播。

第一步:Corona 数据

从下面这个网站上下载最新的 corona 数据(LCTT 译注:2020-12-14 仍可访问,有墙):

我们之间将这个下载好的数据载入为 Pandas 的 DataFrame。Pandas 提供了一个函数, read_csv(),可以直接使用 URL 读取数据,并返回一个 DataFrame 对象,具体如下所示:

import pycountry
import plotly.express as px
import pandas as pd
URL_DATASET = r'https://raw.githubusercontent.com/datasets/covid-19/master/data/countries-aggregated.csv'
df1 = pd.read_csv(URL_DATASET)
print(df1.head(3))  # Get first 3 entries in the dataframe
print(df1.tail(3))  # Get last 3 entries in the dataframe

在 Jupyter 上的输出截图:

 title=

从这个输出可以看到这个 DataFrame(df1)包括以下几列数据:

  1. Date
  2. Country
  3. Confirmed
  4. Recovered
  5. Dead

之后还可以看到 Date 这一列包含了从 1 月 22 日到 3 月 31 日的条目信息。这个数据是每天更新的,所以你会得到你当天的值。

第二步:清理和修改 DataFrame

我们要往这个 DataFrame 中增加一列数据,就是那个包含了 ISO 3 编码。可以通过以下三步完成这个任务:

  1. 创建一个包含所有国家的列表。因为在 df1Country 列中,国家都是每个日期就重复一次。所以实际上 Country 列中对每个国家就会有多个条目。我使用 unique().tolist() 函数完成这个任务。
  2. 我使用 d_country_code 字典对象(初始为空),然后将其键设置为国家的名称,然后它的值设置为其对应的 ISO 3 编码。
  3. 我使用 pycountry.countries.search_fuzzy(country) 为每个国家生成 ISO 3 编码。你需要明白的是这个函数的返回值是一个 Country 对象的列表。我将这个函数的返回值赋给 country_data 对象。以这个对象的第一个元素(序号 0)为例。这个 \ 对象有一个 alpha_3 属性。所以我使用 country_data[0].alpha_3 就能“获得”第一个元素的 ISO 3 编码。然而,在这个 DataFrame 中有些国家的名称可能没有对应的 ISO 3 编码(比如有争议的领土)。那么对这些“国家/地区”,我就用一个空白字符串来替代 ISO 3 编码。你也可以用一个 try-except 代码来替换这部分。except 中的语句可以写:print(‘could not add ISO 3 code for ->', country)。这样就能在找不到这些“国家/地区”对应的 ISO 3 编码时给出一个输出提示。实际上,你会发现这些“国家/地区”会在最后的输出中用白色来表示。
  4. 在获得了每个国家的 ISO 3 编码(有些是空白字符串)之后,我把这些国家的名称(作为键)还有国家对应的 ISO 3 编码(作为值)添加到之前的字典 d_country_code 中。可以使用 Python 中字典对象的 update() 方法来完成这个任务。
  5. 在创建好了一个包含国家名称和对应 ISO 3 编码的字典之后,我使用一个简单的循环将他们加入到 DataFrame 中。

第三步:使用 Plotly 可视化传播路径

choropleth 地图是一个由彩色多边形组成的地图。它常常用来表示一个变量在空间中的变化。我们使用 Plotly 中的 px 模块来创建 choropleth 图,具体函数为:px.choropleth

这个函数的所包含的参数如下:

plotly.express.choropleth(data_frame=None, lat=None, lon=None, locations=None, locationmode=None, geojson=None, featureidkey=None, color=None, hover_name=None, hover_data=None, custom_data=None, animation_frame=None, animation_group=None, category_orders={}, labels={}, color_discrete_sequence=None, color_discrete_map={}, color_continuous_scale=None, range_color=None, color_continuous_midpoint=None, projection=None, scope=None, center=None, title=None, template=None, width=None, height=None)

choropleth() 这个函数还有几点需要注意:

  1. geojson 是一个 geometry 对象(上面函数第六个参数)。这个对象有点让人困扰,因为在函数文档中没有明确地提到这个对象。你可以提供,也可以不提供 geojson 对象。如果你提供了 geojson 对象,那么这个对象就会被用来绘制地球特征,如果不提供 geojson 对象,那这个函数默认就会使用一个内建的 geometry 对象。(在我们的实验中,我们使用内建的 geometry 对象,因此我们不会为 geojson 参数提供值)
  2. DataFrame 对象有一个 data_frame 属性,在这里我们先前就提供了一个我们创建好的df1
  3. 我们用 Confirmed(确诊数)来决定每个国家多边形的颜色。
  4. 最后,我们 Date 列创建一个 animation_frame。这样我们就能通过日期来划分数据,国家的颜色会随着 Confirmed 的变化而变化。

最后完整的代码如下:

import pycountry
import plotly.express as px
import pandas as pd
# ----------- Step 1 ------------
URL_DATASET = r'https://raw.githubusercontent.com/datasets/covid-19/master/data/countries-aggregated.csv'
df1 = pd.read_csv(URL_DATASET)
# print(df1.head) # Uncomment to see what the dataframe is like
# ----------- Step 2 ------------
list_countries = df1['Country'].unique().tolist()
# print(list_countries) # Uncomment to see list of countries
d_country_code = {}  # To hold the country names and their ISO
for country in list_countries:
    try:
        country_data = pycountry.countries.search_fuzzy(country)
        # country_data is a list of objects of class pycountry.db.Country
        # The first item  ie at index 0 of list is best fit
        # object of class Country have an alpha_3 attribute
        country_code = country_data[0].alpha_3
        d_country_code.update({country: country_code})
    except:
        print('could not add ISO 3 code for ->', country)
        # If could not find country, make ISO code ' '
        d_country_code.update({country: ' '})

# print(d_country_code) # Uncomment to check dictionary  

# create a new column iso_alpha in the df
# and fill it with appropriate iso 3 code
for k, v in d_country_code.items():
    df1.loc[(df1.Country == k), 'iso_alpha'] = v

# print(df1.head)  # Uncomment to confirm that ISO codes added
# ----------- Step 3 ------------
fig = px.choropleth(data_frame = df1,
                    locations= "iso_alpha",
                    color= "Confirmed",  # value in column 'Confirmed' determines color
                    hover_name= "Country",
                    color_continuous_scale= 'RdYlGn',  #  color scale red, yellow green
                    animation_frame= "Date")

fig.show()

你可以从这里下载并运行完整代码

最后,这里还有一些关于 Plotly 绘制 choropleth 图的不错的资源。


via: https://opensource.com/article/20/4/python-map-covid-19

作者:AnuragGupta 选题:lujun9972 译者:zhangxiangping 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在过去的几周里,我花了很多时间用 PyTorch 实现了一个 char-rnn 的版本。我以前从未训练过神经网络,所以这可能是一个有趣的开始。

这个想法(来自 循环神经网络的不合理效应)可以让你在文本上训练一个基于字符的 循环神经网络 recurrent neural network (RNN),并得到一些出乎意料好的结果。

不过,虽然没有得到我想要的结果,但是我还是想分享一些示例代码和结果,希望对其他开始尝试使用 PyTorch 和 RNN 的人有帮助。

这是 Jupyter 笔记本格式的代码:char-rnn in PyTorch.ipynb。你可以点击这个网页最上面那个按钮 “Open in Colab”,就可以在 Google 的 Colab 服务中打开,并使用免费的 GPU 进行训练。所有的东西加起来大概有 75 行代码,我将在这篇博文中尽可能地详细解释。

第一步:准备数据

首先,我们要下载数据。我使用的是 古登堡项目 Project Gutenberg 中的这个数据:Hans Christian Anderson’s fairy tales

!wget -O fairy-tales.txt

这个是准备数据的代码。我使用 fastai 库中的 Vocab 类进行数据处理,它能将一堆字母转换成“词表”,然后用这个“词表”把字母变成数字。

之后我们就得到了一个大的数字数组(training_set),我们可以用于训练我们的模型。

from fastai.text import *
text = unidecode.unidecode(open('fairy-tales.txt').read())
v = Vocab.create((x for x in text), max_vocab=400, min_freq=1)
training_set = torch.Tensor(v.numericalize([x for x in text])).type(torch.LongTensor).cuda()
num_letters = len(v.itos)

第二步:定义模型

这个是 PyTorch 中 LSTM 类的封装。除了封装 LSTM 类以外,它还做了三件事:

  1. 对输入向量进行 one-hot 编码,使得它们具有正确的维度。
  2. LSTM 层后一层添加一个线性变换,因为 LSTM 输出的是一个长度为 hidden_size 的向量,我们需要的是一个长度为 input_size 的向量这样才能把它变成一个字符。
  3. LSTM 隐藏层的输出向量(实际上有 2 个向量)保存成实例变量,然后在每轮运行结束后执行 .detach() 函数。(我很难解释清 .detach() 的作用,但我的理解是,它在某种程度上“结束”了模型的求导计算)(LCTT 译注:detach() 函数是将该张量的 requires_grad 参数设置为 False,即反向传播到该张量就结束。)
class MyLSTM(nn.Module):
    def __init__(self, input_size, hidden_size):
        super().__init__()
        self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
        self.h2o = nn.Linear(hidden_size, input_size)
        self.input_size=input_size
        self.hidden = None

    def forward(self, input):
        input = torch.nn.functional.one_hot(input, num_classes=self.input_size).type(torch.FloatTensor).cuda().unsqueeze(0)
        if self.hidden is None:
            l_output, self.hidden = self.lstm(input)
        else:
            l_output, self.hidden = self.lstm(input, self.hidden)
        self.hidden = (self.hidden[0].detach(), self.hidden[1].detach())

        return self.h2o(l_output)

这个代码还做了一些比较神奇但是不太明显的功能。如果你的输入是一个向量(比如 [1,2,3,4,5,6]),对应六个字母,那么我的理解是 nn.LSTM 会在内部使用沿时间反向传播更新隐藏向量 6 次。

第三步:编写训练代码

模型不会自己训练的!

我最开始的时候尝试用 fastai 库中的一个辅助类(也是 PyTorch 中的封装)。我有点疑惑因为我不知道它在做什么,所以最后我自己编写了模型训练代码。

下面这些代码(epoch() 方法)就是有关于一轮训练过程的基本信息。基本上就是重复做下面这几件事情:

  1. 往 RNN 模型中传入一个字符串,比如 and they ought not to teas。(要以数字向量的形式传入)
  2. 得到下一个字母的预测结果
  3. 计算 RNN 模型预测结果和真实的下一个字母之间的损失函数(e,因为 tease 这个单词是以 e 结尾的)
  4. 计算梯度(用 loss.backward() 函数)
  5. 沿着梯度下降的方向修改模型中参数的权重(用 self.optimizer.step() 函数)
class Trainer():
  def __init__(self):
      self.rnn = MyLSTM(input_size, hidden_size).cuda()
      self.optimizer = torch.optim.Adam(self.rnn.parameters(), amsgrad=True, lr=lr)
  def epoch(self):
      i = 0
      while i < len(training_set) - 40:
        seq_len = random.randint(10, 40)
        input, target = training_set[i:i+seq_len],training_set[i+1:i+1+seq_len]
        i += seq_len
        # forward pass
        output = self.rnn(input)
        loss = F.cross_entropy(output.squeeze()[-1:], target[-1:])
        # compute gradients and take optimizer step
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

使用 nn.LSTM 沿着时间反向传播,不要自己写代码

开始的时候我自己写代码每次传一个字母到 LSTM 层中,之后定期计算导数,就像下面这样:

for i in range(20):
    input, target = next(iter)
    output, hidden = self.lstm(input, hidden)
loss = F.cross_entropy(output, target)
hidden = hidden.detach()
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

这段代码每次传入 20 个字母,每次一个,并且在最后训练了一次。这个步骤就被称为沿时间反向传播,Karpathy 在他的博客中就是用这种方法。

这个方法有些用处,我编写的损失函数开始能够下降一段时间,但之后就会出现峰值。我不知道为什么会出现这种现象,但之后我改为一次传入 20 个字符到 LSTM 之后(按 seq_len 维度),再进行反向传播,情况就变好了。

第四步:训练模型!

我在同样的数据上重复执行了这个训练代码大概 300 次,直到模型开始输出一些看起来像英文的文本。差不多花了一个多小时吧。

这种情况下我也不关注模型是不是过拟合了,但是如果你在真实场景中训练模型,应该要在验证集上验证你的模型。

第五步:生成输出!

最后一件要做的事就是用这个模型生成一些输出。我写了一个辅助方法从这个训练好的模型中生成文本(make_predsnext_pred)。这里主要是把向量的维度对齐,重要的一点是:

output = rnn(input)
prediction_vector = F.softmax(output/temperature)
letter = v.textify(torch.multinomial(prediction_vector, 1).flatten(), sep='').replace('_', ' ')

基本上做的事情就是这些:

  1. RNN 层为字母表中的每一个字母或者符号输出一个数值向量(output)。
  2. 这个 output 向量并不是一个概率向量,所以需要 F.softmax(output/temperature) 操作,将其转换为概率值(也就是所有数值加起来和为 1)。temperature 某种程度上控制了对更高概率的权重,在限制范围内,如果设置 temperature=0.0000001,它将始终选择概率最高的字母。
  3. torch.multinomial(prediction_vector) 用于获取概率向量,并使用这些概率在向量中选择一个索引(如 12)。
  4. v.textify12 转换为字母。

如果我们想要处理的文本长度为 300,那么只需要重复这个过程 300 次就可以了。

结果!

我把预测函数中的参数设置为 temperature = 1 得到了下面的这些由模型生成的结果。看起来有点像英语,这个结果已经很不错了,因为这个模型要从头开始“学习”英语,并且是在字符序列的级别上进行学习的。

虽然这些话没有什么含义,但我们也不知道到底想要得到什么输出。

“An who was you colotal said that have to have been a little crimantable and beamed home the beetle. “I shall be in the head of the green for the sound of the wood. The pastor. “I child hand through the emperor’s sorthes, where the mother was a great deal down the conscious, which are all the gleam of the wood they saw the last great of the emperor’s forments, the house of a large gone there was nothing of the wonded the sound of which she saw in the converse of the beetle. “I shall know happy to him. This stories herself and the sound of the young mons feathery in the green safe.”

“That was the pastor. The some and hand on the water sound of the beauty be and home to have been consider and tree and the face. The some to the froghesses and stringing to the sea, and the yellow was too intention, he was not a warm to the pastor. The pastor which are the faten to go and the world from the bell, why really the laborer’s back of most handsome that she was a caperven and the confectioned and thoughts were seated to have great made

下面这些结果是当 temperature=0.1 时生成的,它选择字符的方式更接近于“每次都选择出现概率最高的字符”。这就使得输出结果有很多是重复的。

ole the sound of the beauty of the beetle. “She was a great emperor of the sea, and the sun was so warm to the confectioned the beetle. “I shall be so many for the beetle. “I shall be so many for the beetle. “I shall be so standen for the world, and the sun was so warm to the sea, and the sun was so warm to the sea, and the sound of the world from the bell, where the beetle was the sea, and the sound of the world from the bell, where the beetle was the sea, and the sound of the wood flowers and the sound of the wood, and the sound of the world from the bell, where the world from the wood, and the sound of the

这段输出对这几个单词 beetlesconfectionerssunsea 有着奇怪的执念。

总结!

至此,我的结果远不及 Karpathy 的好,可能有一下几个原因:

  1. 没有足够多的训练数据。
  2. 训练了一个小时之后我就没有耐心去查看 Colab 笔记本上的信息。
  3. Karpathy 使用了两层LSTM,包含了更多的参数,而我只使用了一层。
  4. 完全是另一回事。

但我得到了一些大致说得过去的结果!还不错!


via: https://jvns.ca/blog/2020/11/30/implement-char-rnn-in-pytorch/

作者:Julia Evans 选题:lujun9972 译者:zhangxiangping 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出