Saturday, May 23, 2020

在 Windows 10 下如何用键盘快捷键管理窗口?

macOS 的各种键盘快捷键十分好用,而用 Windows 习惯点鼠标,如果开了好多窗口的话只点鼠标来切换或定位窗口十分不方便。其实 Windows 10 下面也有很多键盘快捷键来切换、定位、最小化、最大化、移动或调整窗口大小来安排你的工作区域,比用鼠标来得方便多了。

在窗口之间切换

Windows 10 有一个“任务切换器 Task Switcher”,随时按 Alt + Tab,所有打开的窗口的缩略图都会显示在屏幕上。要循环选择各个窗口,按住 Alt 键,然后按 Tab 键,直到选中想要的窗口。释放两个键,此窗口将进入前台。

您也可以按 Ctrl + Alt + Tab 打开任务切换器。然后,使用箭头键选择所需的窗口,然后按 Enter。

在窗口之间切换还有一种方法“任务视图 Task View”。它会占用更多的屏幕,并显示所有打开的窗口的预览。要打开任务视图,请按 Windows + Tab,然后使用箭头键选择要查看的窗口,然后按 Enter。您选择的窗口被带到前台。

窗口的最小化和最大化

仅使用键盘就可以最小化或最大化窗口。最小化可隐藏窗口,而最大化可扩大窗口,使其占据整个屏幕。您还可以同时最小化所有窗口,以便可以看到桌面。

使用以下快捷方式:

  • 最小化当前窗口:Windows + 向下箭头。
  • 最大化当前窗口:Windows + 向上箭头。
  • 最小化所有窗口:Windows + M。
  • 最小化所有窗口并显示桌面:Windows + D。(这也适用于顽固的窗口)。
  • 最小化除当前窗口外的所有窗口:Windows + Home。
  • 还原所有最小化的窗口:Windows + Shift + M。

您也可以放大没有最大化的窗口。如果要将当前窗口的高度(而不是宽度)拉伸到屏幕的顶部和底部,请按 Windows + Shift + 向上箭头。请注意,如果窗口被捕捉到下面要讲的四分之一视角位置,则此快捷方式将不起作用。

将窗口对齐到屏幕的一半或者四分之一

如果您打开了多个窗口,并且想使用键盘快捷键来将它们准确地显示在屏幕上。您可以轻松地将两个窗口定位到屏幕的一半,或者将四个窗口定位到屏幕的四分之一区域。

首先,按 Alt + Tab 或使用鼠标将要重新定位的窗口置于焦点上。从那里,确定您希望该窗口占据屏幕的哪一部分。

然后,您可以使用以下快捷方式将两个窗口分成两半:

  • 定位到左侧一半:Windows + 向左箭头。
  • 定位到右侧一半:Windows + 向右箭头。

要将四个窗口放置在四分之一的位置(每个窗口将占整个屏幕的 1/4),可以使用两个快捷键序列。这些顺序假定窗口尚未定位到屏幕的左半部分或右半部分。

方法如下:

  • 左上四分之一:Windows + 向左箭头,然后是 Windows + 向上箭头。
  • 左下四分之一:Windows + 向左箭头,然后是 Windows + 向下箭头。
  • 右上四分之一:Windows + 向右箭头,然后是 Windows + 向上箭头。
  • 右下四分之一:Windows + 向右箭头,然后是 Windows + 向下箭头。

精确地移动窗口

您可以使用键盘将某个窗口移至屏幕上的特定位置。首先,按 Alt + Tab 选择要移动的窗口。选择窗口后,按 Alt + Space 可以在左上角打开一个小菜单。按箭头键选择“移动”,然后按 Enter。使用箭头键将窗口移动到屏幕上所需的位置,然后按 Enter。

即使您要移动的窗口处于隐藏状态并且您无法用鼠标找到它,此技巧仍然有效。

在显示器之间移动窗口

如果您使用多台显示器,并且在它们之间扩展了桌面(不是镜像了桌面),那么你可以在显示器之间快速移动活动窗口。按Windows + Shift + 向左 或 + 向右箭头就可以了。

窗口管理键盘快捷键速查表

这是上面介绍的所有内容的速查表。

  • Alt + Tab:打开任务切换器。
  • Windows + Tab:打开任务视图。
  • Windows + 向下箭头:最小化窗口。
  • Windows + 向上箭头:最大化窗口。
  • Windows + M:最小化所有窗口。
  • Windows + D:显示桌面。
  • Windows + Home:最小化除活动窗口外的所有窗口。
  • Windows + Shift + M:还原所有最小化的窗口。
  • Windows + Shift + 向上箭头:将窗口拉伸到屏幕的顶部和底部。
  • Windows + 向左箭头:最大化屏幕左侧的窗口。
  • Windows + 向右箭头:最大化屏幕右侧的窗口。
  • Windows + Shift + 向左或向右箭头:将窗口从一台显示器移到另一台。

Thursday, May 21, 2020

Python 3 的 x//y 和 x%y

Python 3 的 // 和 %

Python for Data Analysis 第二章第 37 页提到了 a // b,但是没提到 a % b。在 Jupyter Notebook 上试试 -10 // 3 是什么结果, -10 % 3 是什么结果。再试试 10 // -3 和 10 % -3 是什么结果。然后问问自己为什么,不懂的话就去网上找答案。答案在这里。我摘录如下:

The / (division) and // (floor division) operators yield the quotient of their arguments. The numeric arguments are first converted to a common type. Division of integers yields a float, while floor division of integers results in an integer; the result is that of mathematical division with the ‘floor’ function applied to the result. Division by zero raises the ZeroDivisionError exception.

The % (modulo) operator yields the remainder from the division of the first argument by the second. The numeric arguments are first converted to a common type. A zero right argument raises the ZeroDivisionError exception. The arguments may be floating point numbers, e.g., 3.14%0.7 equals 0.34 (since 3.14 equals 4*0.7 + 0.34.) The modulo operator always yields a result with the same sign as its second operand (or zero); the absolute value of the result is strictly smaller than the absolute value of the second operand.

The floor division and modulo operators are connected by the following identity: x == (x//y)*y + (x%y). Floor division and modulo are also connected with the built-in function divmod(): divmod(x, y) == (x//y, x%y).

/ 除法很简单,无论被除数和除数是浮点数还是整数,结果都是浮点数。

// 地板除法有点复杂。被除数和除数都是整数,结果一定是整数。这个整数是怎么来的呢?就是商值经过 Python 内置的地板函数 math.floor(x) 过一道手续。而这个函数在 Python 文献里如何解释的呢?

math.floor(x)
Return the floor of x as a float, the largest integer value less than or equal to x.

-10 //  3 = -3.333... 比它小的整数(总在它左边)是 -4。

同样,10 // -3 结果也是 -4。

而 % 的结果必须与除数同正负,且绝对值必须比除数小。

-10 = 3 x (-4) +2,所以 -10 % 3 = 2。如果 -10 = 3 x (-3) + (-1),就不对了。

而 10 = (-3) x (-4) + (-2),-2 与 -3 同为负,所以 10 % -3 = -2。如果 10 = (-3) x (-3) + 1,1 与 -3 一正一负,就不对了。

请注意 Python 的 math.fmod(x, y) 函数是按照 C 标准写的,得出的结果与 Python 的 % 不一样。请看 Python Mathematical functions 的详细文献。

内建函数:divmod(x, y) 可以返回 x//y 和 x%y 的结果:x, y 是两个数,这个函数返回一个元组,这个元组为 (x//y, x%y)。

In [1]: divmod(10, -3)
Out[1]: (-4, -2)

In [2]: divmod(-10, 3)
Out[2]: (-4, 2)

 

Wednesday, May 20, 2020

macOS Catalina 下的 Python 3 和 Anaconda

系统环境

  • MacBook Pro (Retina, 13-inch, Late 2013)
  • macOS Catalina Version 10.15.4

查看 macOS 内的自带 Python,你会惊讶 Catalina 现在还自带了一个 Python 3,版本号还不低。这里有人讨论

% which python
/usr/bin/python
% which python3
/usr/bin/python3
% python --version

Python 2.7.16
% python3 --version
Python 3.7.3

默认的还是过时的 Python 2。如果你刚开始学 Python,没关系,直接用 python3 启动解释器就可以学习了。不过,这个 Python 3 没有 IDLE。

如果你要在 macOS 上新建一个 Python 开发环境,怎么办?请仔细读一下这篇文章

"The basic premise of all Python development is to never use the system Python. You do not want the Mac OS X 'default Python' to be 'python3.' You want to never care about default Python."

但是......这篇文章也没有解决一个问题,我现在要用 Anaconda,conda 和 pyenv 两者之间有冲突,这个怎么办?看看这个讨论。也看看这篇文章 Michael Sarahan 的建议,都是一个意思。

我是怎么做的呢?

先根据这篇文章的说明用 pyenv 安装了最高可安装版本 python 3.8.2,因此里面的 3.8.1 改成 3.8.2。最后一步需要 restart 你的 terminal 后 python -V 才能显示最新安装的版本。请也查查 pip 版本。

然后在 homebrew 下安装 Anaconda

~ brew cask install anaconda

安装完成后要对 Anaconda 进行初始化,初始化的理由看上面 Michael Sarahan 的说明。我在这里引述一下:

“Modifying PATH can cause problems if there are any other programs on your system that have the same names, that Anaconda then hides (shadows) by being found first. What “conda init” does is to set up a conda “shell function” and keep the other stuff off PATH. Nothing but conda is on PATH. It then defaults to activating your base environment at startup. The net effect is very much like your PATH addition, but has some subtle, but critically important differences:

activation ensures that anaconda’s PATH stuff is right up front. Putting anaconda at the front of PATH permanently is good in that it prevents confusion, but bad in that it shadows other stuff and can break things. Activation is a less permanent way to do this. You can turn off the automatic activation of the base environment using the “auto_activate_base” condarc setting.

activation does a bit more than just modifying PATH. It also sources any activate.d scripts, which may set additional environment variables. Some things, such as GDAL, require these. These packages will not work without activation.

So, rather than your tip on modifying PATH, what we recommend is following the directions at the end of the installer.

Should you choose not to run conda init:

that will look like 2 commands:

1. eval “$(/home/msarahan/mc3_dummy/bin/conda shell.bash hook)”

2. conda init

Note that in step 1, I changed the shell name from YOUR_SHELL_NAME to bash. You may need to adjust that yourself to your shell.”

如何自己手动初始化?在 Anaconda 的官方网站上现在有介绍,请看 Installing on macOS 的第 7 步

If you enter “no”, then conda will not modify your shell scripts at all. In order to initialize after the installation process is done, first run source <path to conda>/bin/activate and then run conda init.

If you are on macOS Catalina, the new default shell is zsh. You will instead need to run source <path to conda>/bin/activate followed by conda init zsh.

我用的 zsh,所以先:

~ source /usr/local/anaconda3/bin/activate
(base) ~ conda init zsh

你会看到 prompt 多了一个 (base),表示目前在 Anaconda base env 下。

重新启动 terminal,然后再确认一下是否正常工作:

(base) ~ conda
(base) ~ ipython
(base) ~ jupyter notebook
(base) ~ python

你会看到都能正常运行,而且 python 现在是 Anaconda 带的版本,而不是之前安装安装的 3.8.2 版本。回到 3.8.2 版本,要退出 Anaconda base env,使用下面的命令:

(base) ~ conda deactivate

(base) 消失,再查看就是默认的 Python 版本了。

~ python -V

现在 homebrew 有一个 python 3.8.2,还有一个 Anaconda。两个互不干扰,你可以在各自的环境下建立 Python Virtual Environment。

初始化后,查看 .zshrc 文件看看发生了什么变化:

~ less .zshrc

有一大块 shell script:

# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
...
# <<< conda initialize <<<

现在你每次启动 terminal 都会自带打开 Anaconda 的 base env。prompt 前面会出现一个 (base)。你可以修改 conda 的配置文件 .condarc 关闭自动启动。看官方说明如何修改。下面的命令即可:

(base) ~ conda config --set auto_activate_base False
(base) ~ conda deactivate

重启 terminal,再打开已经看不到 (base) 了。

再进入 Anaconda 下,输入以下命令:

~ conda activate

Python for Data Analysis 第三章

In [1]: a = {1:2, 2:3, 3:4}

In [2]: tuple(a)
Out[2]: (1, 2, 3)

tuple() 对 dict 强制类型转换是取 keys。

In [22]: a = (1, 2, 3, 4, 3, 4)

In [23]: a.index(3)
Out[23]: 2

tuple 还有一个 index method:

tuple.index(element)

找到的是第一个出现的 element。

dict() 函数

The dict() constructor builds dictionaries directly from sequences of key-value pairs.

>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}

如果 a = ['sape', 'guido', 'jack'],b = [4139, 4127, 4098],那么如何变成 [('sape', 4139), ('guido', 4127), ('jack', 4098)]?用前面学过的 zip() 函数。记住 zip() 是个 lazy iterator

>>> a = ['sape', 'guido', 'jack']
>>> b = [4139, 4127, 4098]
>>> zip(a, b)
>>> type(zip(a, b))
<class 'zip'>
>>> dict(zip(a, b))
{'sape': 4139, 'guido': 4127, 'jack': 4098}

这种方式叫 dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs。

>>> {zip(a, b)}
{<zip object at 0x108924280>}

用花括号 {} 就无法构建出一个这样的 dictionary。但是用 {} 和 dict() 都可以构建一个空字典。

dict() 函数还有一种构建字典的方式叫 dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list.

>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}

dict() 函数还有一种构建字典的方式叫 dict(iterable)。iterable 可以用 dict comprehension 来实现。

>>> dict((x, x**2) for x in (2, 4, 6))
{2: 4, 4: 16, 6: 36}

这种方式也可以直接用 {} 来操作:

>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

看看两个用法还是有区别的。

参考文献[1]和[2]

第 89 页:为什么 numpy.empty() 生成的不是非空的 array?看这里的讨论

第 91 页:Numpy data type 也可以延续用 Python 的 int, float, str, bool, bytes ... 等数据类型

int 对应的是 numpy.init32

In [1]: import numpy as np

In [2]: a = np.array([1, 2, 3], dtype=int)

In [3]: a.dtype
Out[3]: dtype('int32')

float 对应的是 numpy.float64

In [4]: a = np.array([1, 2, 3], dtype=float)

In [5]: a.dtype
Out[5]: dtype('float64')

str 对应的是 numpy.unicode_

In [7]: a = np.array([1, 2, 3], dtype=str)

In [8]: a.dtype
Out[8]: dtype('<U1')

In [9]: a = np.array(['1', '2', '30000'], dtype=str)

In [10]: a.dtype
Out[10]: dtype('<U5')

第 103 页:传递多个索引数组会有所不同。

In [17]: arr = np.array([[1, 2, 3, 4], [2, 4, 6, 8], [3, 6, 9, 12], [4, 8, 12, 16]])

In [20]: arr[[0, 2], [1, 2]]
Out[20]: array([2, 9])

相当于取第一行和第二列 (0, 1),和取第三行和第三列 (2, 2) 的元素然后组成了一个一维 numpy.array。

Python for Data Analysis 2.3 节概括

Python for Data Analysis 2.3 节概括

2.3 节是 Python 语言基础,还是在这里概括一下,方便以后查询。

首先,Python 的缩进(indentation)十分重要,相当于 C/C++ 的花括号,定义了作用域。PEP 8 规范是 Python 的官方代码样式指南,值得读一下。缩进建议是 4 个空格 spaces,尽量不要用 tab。

Colons 冒号 : 后面是缩进的代码块。

多个语句可以用 Semicolons 分号 ; 隔开写在一行上,但是不要这么做。

Python 语言里一切东西都是 object,在解释器里都有一个存储“盒”。

Hash mark or pound sign 井号 # 后空一格开始注释。多行注释形成注释块 block comments行内注释 inline comments 在代码后面。

调用函数 function 和 object 的方法 method,并给变量赋值。函数的位置参数关键字参数

变量赋值 variable assignment 的深入理解请看这篇文章

理解了变量的赋值,理解 函数的参数传递 argument passing 就容易多了,也就是 形参 arguments 指向了 实参 parameters 指向的 object,或者说同一个 object 有两个 references,一个 reference 让形参找到自己,另外一个 reference 让实参找到自己。

所以,在函数域内,给形参赋予另外一个 object 后,形参就指向到这个 object 了,形参 reference 变了,形参和实参就脱离了关系。

如果传递的参数是可变类型,比如 list,那么在函数域内可能会改变了 list 内的内容,但是形参 reference 没变,也就是还是指向实参所指向的那个 list,这样就影响到了函数外的 object 了。不得已,不要这么做。

Object 都有类型,但是它的编号 reference 是没类型的。既然 python 里所有东西都是 object,所以 python 是强类型语言,但是你不用关心类型,一切都隐式的储藏着。

变量 Variable 是给 Object 起的名字,都有 namespace,出了这个 namespace 就不起作用了。

isinstance() 函数的用法。

object 的 属性 attibutes方法 methods。dir()、getattr()、hasattr()、setattr() 的基本用法。

Duck typing 网上叫鸭子类型;动态类型;行为决定类型,我不懂。意思是虽然你不需要那么关心 object 的类型,但是你需要知道 object 的方法(行为),只有这样你就知道如何用这个 object 了,否则这个 object 对你有什么用处?第 35 页上的那个 isiterable(obj) 函数好好学习一下,配合 isinstance() 使用。

模块 Module 和它的多种导入使用方法。

二元操作符 binary operators 和比较。关键词 is 和 关键词 is not (对比编号指向的是否是同一 object),它们与 == (对比 objects 内容是否一样)的区别。

可变对象 mutable objects: lists, dicts, arrays, Numpy arrays, pandas series, pandas dataframes, classes...

不可变对象 immutable objects: strings, tuples

标量类型 Scalar Types: NoneType - None, str, bytes, float, bool - True/False, int, complex...

单引号 ' 双引号 " 包围下就是字符串 string literals。含换行符的多行字符串可以用三个单引号 ''' 或者双引号 """ 包围起来。换行符不显示出来,但是包含在里面,查看有多少行,可以 print() 或者 repr()。

backslash character 反斜杠字符 \ 也叫转义符号 escape character。

raw string 表示方法。

str 类型 object 有很多 methods,查看 python 文献

str 类型 format 的方法的基本用法,更深的解释请看这个

bytes 类型 和 Unicode 字符串之间的互换,也就是 encode 和 decode 方法。

bool 类型的两个布尔值分别是 TrueFalse,首字母大写。

None 是保留关键字,也是 NoneType 的一个实例 Instance。

datetime 和 date 和 time 在后面章节里会更详细的讲解。Table 2-5 后面会参考到。

Control flow 流程控制与别的程序语言大同小异,但是 Python 对 Sequence 进行 loop 循环的时候有特别的优势,需要好好体会一下。对 if 控制流程、range() 函数、break/continue、pass 等请看这里。对 while、for、try、with 等看这里

Tenary expression 三元表达式。

Type casting 类型转换

Type casting 类型转换的几个函数 str()、float()、int()、bool()内置的 eval() 函数也可以把字符串转换成整数,但是尽量不要用 eval()

tuple()、list()、dict()、numpy.array()、numpy.arange()、pandas.Series()、pandas.DataFrame() 等都可以把其它类型的 object 转换成各自的数据结构类型,也可以说是类型转换。

Python 的 Try Exception

第 35 页的 isiterable 函数中用到了 Try Exception,在书中第 77 页有讲解。

def isiterable(obj):
    try:
        iter(obj)
        return True
    except TypeError: # not iterable
        return False

这里的一个知识点是 Python 的错误 Errors 和异常 Exceptions,及如何处理的,可以看 Python 官方文献这篇文章也写得很好。

Tuesday, May 19, 2020

Python for Data Analysis 笔记

我在 2019 年 1 月 15 日从 Amazon.ca 买的《Python for Data Analysis - Data Wrangling with Pandas, Numpy, and iPython》这本书,买来就闲置着,没有看过。这次 COVID-19 大家都得呆在家里,没什么事干,决定把这本书干下来。

作者是 Wes McKinney,他是 Pandas 开源软件包的创建者。他在 2012 年 10 月首次出版了这本书,在 2017 年出了第二版,第二版又发行了两次,9 月 25 日版本和 10 月 20 日版本。我买的是 10 月 20 日纸质平装本。

这本书比较权威,因此很容易搜到它的电子书,不过我喜欢实实在在的书。

前言 Preface

前言基本上是废话,但需要记住下面两点:

第一章 Preliminaries

这一章介绍了一些预备知识。

如果你以前没接触过 Numpy、pandas、matplotlib、Jupyter、SciPy、scikit-learn 和 statsmodels,1.1 到 1.3 读了也是白读,建议直接略过

1.5 和 1.6 是废话,建议直接略过。

1.4 讲安装和设置,因为Ananconda 自带 Python,这节就是教你在 Windows、macOS 和 Linux 上如何安装 Anaconda,如何安装和更新软件包。它写得太简单了,不要读它了。网上很多更好的最新的教程,自己去搜。这是本章你唯一需要做的事。

在 macOS 上安装 Anaconda 请注意看我写的这篇文章

在 Windows 上,可以按照 Anaconda 的官方安装指南来安装启动

第二章 Python 语言基础,IPython 和 Jupyter Notebook

我相信你以前肯定学习过 Python 语言。如果没学过,建议你找本书学习一下。如果你学习过其它程序语言,看完第二章和第三章也应该可以。

我学习过最基本的 Python,算是个初级者,但是没用过 IPython shell,更没用过 Jupyter Notebook。如果你也和我一样,那么你要好好读读 2.1 到 2.2 节,因为全书都是基于 Jupyter Notebook 的。

如果你正常安装了 Anaconda,那么 python shell,ipython shell 就能运行起来,jupyter notebook 也能运行起来。如果运行不起来,根据出现的错误提示网上搜索解决方法。

请注意:在 Windows 下需要先启动 Anaconda prompt,然后才能运行 python、ipython 和 jupyter notebook。在 Windows 下的 cmd、powershell 或 terminal 下运行的 python 可能是你安装的其它版本的 python。

2.3 是 Python 语言基础知识。根据你自身情况决定略过与否。

此章重点:

  • 启动 Anaconda 的 python interpreter/shell
  • 启动 ipython interpreter/shell
  • 启动 jupyter notebook,执行 python 代码,关闭 jupyter notebook。
  • 掌握 ipython 和 jupyter notebook 下的 Tab Completion、Introspection、%run Command、Magic Command
  • 掌握 ipython 和 jupyter notebook 下的 matplotlib integration。对比一下 %matplotlib inline 和 %matplotlib 显示结果的不同。

以后的章节都在 jupyter notebook 上运行。

体验一下在 Windows 下 shut down Jupyter Notebook 有时十分慢,是件痛苦的事情。

第三章 Python 语言内置数据结构,函数和文件

这一章还是讲 Python 语言的。如果你和我一样是 Python 语言初级者,值得复习一下这部分,否则略过。

自我学习

看到不是十分明白的地方,要善于自我学习。IPython、NumPy 和 pandas 官方网站上都有搜索框,可以帮助你深入学习。

另外,在网上也可以搜到对某个知识点的深度解释和举例。

在读这本书的过程中,Jupyter Notebook 要一直开着,Google 要一直开着。

举一个例子

比如在第 18 页,有下面这样两行代码:

import numpy as np

data = {i : np.random.randn() for i in range(7)}

NumPy 在第四章才开始讲,但是你想知道 numpy.random.randn() 做什么的,你可以把它复制到 NumPy Documentation 上去搜出参考文献。同时你也会看到这样一个提示:New code should use the standard_normal method of a default_rng() instance instead; see random-quick-start。这样你就会搜到 Random sampling (numpy.random)numpy.random.Generator.standard_normal 等参考文献。

已经不建议使用 numpy.random.randn() 了。如果使用新的代码,第二行这样写比较好:

rng = np.random.default_rng()

data = {i : rng.standard_normal() for i in range(7)}

更多例子

比如上面提到的 %matplotlib 和 %matplotlib inline 的区别(在书中第 30 页)。你就会搜到 IPython 官方文献对 %matplotlib 这个 magic command 的详细解释,和网上其他人的答疑。

还有第 32 和 33 页提到的 Python 的变量赋值和函数的参数传递机制,如果不是很明白,Google 更多的例子看看,或者回头去看看专门的 Python 书籍。

避免成为早期用户的一些规则

“早期用户”是个商业术语,对应的英文是“Early Adopter”,是指那些早于别人第一批使用新产品或新技术的实体(个人或者企业)。比如 Apple iPhone 新版本一出来就去抢着买的人,新操作系统一出来就去安装的人,他们都是“早期用户”。

早期用户往往因为尝鲜要支付过高的“溢价”。早期用户也往往是实验对象(Guinea Pigs),是自愿的产品检测者。因此我在此制定了一些规则,希望以后有冲动的时候提醒一下自己。

Apple 硬件产品

不要在产品发布当天购买产品,等一两个月,等所有制造问题得到解决,在此期间看看别人对发布后产品的综评,在 Twitter 上搜索用户的实时反馈,看看产品是否有新生代的代级缺陷。

避免购买有重大更新的第一代产品,购买那些经过考验的的产品。比如 iPhone 6 Plus 是苹果公司的第一代超大屏手机,不出问题才怪。我是它出来几个月后去买的,等的时间还不够,问题很多,后来成为我的废得最快的 iPhone 手机。

iPhone X 要推出了,也有很大的变化,新的屏幕,Face ID 和其它新颖的特性,意料之中,肯定会出现硬件问题。而 iPhone 8 和 iPhone 8 Plus 是自 iPhone 6 以来同类系列的终极产品,经过不断打造,是最成熟的产品。你急需一部新手机,如何选择?如果你不想支付早期用户成本,如果你是理智的消费者,肯定选择 iPhone 8 系列。我的 iPhone X 送给我孩子用了,我去买了一个 iPhone XS Max。

Apple 软件产品

macOS 新版本至少要等几个月。在这段时间里,苹果公司将会修复成百上千个令人讨厌的漏洞。你不升级,这些漏洞就不会影响到你。另外,你使用的各种插件和自定义扩展需要时间来更新以与新版本的 macOS 兼容。

macOS Catalina,也就是 10.15 版本,是最近的一次代级更新,在短短的 14 天时间内发布了三个更新

  • 2019 年 10 月 7 日 Original Software Update release 原始软件更新版本
  • 2019 年 10 月 15 日 Supplemental update 补充更新
  • 2019 年 10 月 21 日Revised Supplemental update 修订的补充更新

你是否觉得有点头大?2020 年 1 月 28 日发布了 10.15.3,你至少要等到这个版本才应该考虑升级。

同样,推迟升级重大的 iOS 版本,至少等到 x.1、x.2 甚至 x.3 版本发布。如果安全更新还没触及到 iOS 之前的版本,还可以再等等。请记住:iOS 升级后就无法还原,反悔都来不及了。

Sunday, May 17, 2020

航空公司要多久才能恢复到疫前水平?

美国几大航空公司的股票跌得很惨。

Southwest Airlines 西南航空 LUV
Delta Air Lines 达美航空 DAL
United Airlines 联合航空 UAL
American Airlines 美国航空 AAL
Alaska Air Group 阿拉斯加航空 ALK
JetBlue Airways 捷蓝航空 JBLU
Allegiant Air 忠实航空 ALGT
SkyWest Airlines 天西航空 SKYW
Spirit Airlines 精神航空 SAVE
Mesa AIr 梅萨航空 MESA
Air Canada 加拿大航空 AC

下面是四大美国航空公司 2020 年 5 月 15 日周五和 2 月 12 日周三收盘价和跌幅:

公司代码 2020-05-15 2020-02-12 跌幅
LUV 23.87 58.26 -59.03%
DAL 19.19 59.06 -67.51%
UAL 19.92 82.20 -75.77%
AAL 9.04 30.47 -70.33%

2020 年 1 月 31 日周五早上 AAL 宣布立即暂停中美之间的航班,DAL 和 UAL 将在 2 月 6 日起暂停中美之间的航班,之后几小时美国联邦政府宣布立即开始对从中国进入美国的航班采取限制措施。

看看四大航空公司在此之后的股价历史,股民在 2 月份有机会空仓,但是没有谁是神,巴菲特 2020 年在 2 月 27 日还加仓了一点 DAL,所以你如果没有从这个窗口期逃出来也很正常。

巴菲特在 2020 年 5 月 2 日周六说他把持有的四大航空公司股票都卖了。他说:

  • 强制停航对航空公司伤害很大。潜台词:何时复航?
  • 三到四年后飞行里程数能恢复到 2019 年水平吗?潜台词:人们会尽量少坐飞机。
  • 飞机太多了。潜台词:竞争太激烈。
  • 即使恢复了 80% 的水平,但是平均每家航空公司需要借债 100 到 120 亿债务度过这次危机。潜台词:赚的钱还不够抵债和利息。

巴菲特买几家航空公司股票大约花了 70 到 80 亿,以大约 40 亿抛出,亏损大约 50%。

2020 年 5 月 11 日 NBC News 记者 Savannah Guthrie 采访波音公司 CEO Dave Calhoun:

你认为可能会有一家美国主要航空公司倒闭吗?
Savannah Guthrie: Do you think there might be a major U.S. carrier that just has to go out of business?

是的,很有可能。
Dave Calhoun: Yes, most likely

2020 年 5 月 13 日,难得一见市场上有人航空公司股票,此人是 Bill Miller,对他的背景大家可以看看他的 Wikipedia 介绍。他的理由很简单,疫苗出来后人们就会坐飞机,人们喜欢坐飞机。航空公司的股价已经计入了下面两个季度的风险。

2020年 5 月 14 日国际航空运输协会(IATA)主席警告:航空旅行要到 2023 年才能恢复到危机前的水平。远程航空旅行要到 2024 年才能恢复到 2019 年水平。有兴趣的人可以看看中文版,对行业的“收入客公里”的恢复很悲观。

2020 年 5 月 18 日周一航空公司股票大涨,市场大盘也大涨。第一,市场认为疫苗会很快出来。第二,各国逐步采取宽松隔离措施,公司企业分阶段逐步重新开业。加拿大今天是维多利亚日,不开盘。

代码 2020-05-18 单日涨幅
AAL 9.87 +9.18%
DAL 21.86 +13.91%
UAL 24.13 +21.13%
LUV 27.09 +13.49%

 

2020 年 5 月 19日周二。今天加拿大股市开了,加拿大航空公司补涨,收盘 $16.69,涨 $2.07,涨幅 14.16%。美股尾盘跳水。加拿大 S&P/TSX 平稳。AAL 跌 -2.33%,UAL 跌 -1.86%,DAL 跌 -0.46%。LUV 继续表现出色,涨 +2.25%,原因很简单,它主要运行美国国内航线,短程旅行恢复快,受国际禁航的影响小。