家里爆水管的救命神器

家里爆水管的救命神器

家里爆水管怎么办?

在美国当home owner,就总会碰到这样或那样的问题。爆水管就是其中一项。这不,今天就碰到了。我家前院的一个水龙头因为老化,直接就给冲喷了。前院直接就水漫金山了。而且祸不单行的是,那个总闸好像还坏了。我还算镇静,想到水表那应该有开关。结果发现那个开关是个长条形的,根本拧不动。只好打电话给当地自来水公司。幸好我们的墨西哥邻居出手相助,用了一个超大的扳手给关上了。因为自来水公司他们只来帮关闸,不管维修,所以就再打电话给他们说不用来了。他们反应很慢,不知道还要等几个小时呢。

救命神器

后来我买来新的水龙头换上,但是邻居不在,总不能老麻烦人家。如果Google了一下这个阀门应该有什么工具。结果还真给我找到了。这个工具叫water meter key

如果家里curb side的水表的阀门是这种长条类型的,强烈建议花10几刀给家里备上这么个救命神器。真到爆水管的关键时候能把总闸关了。叫一次plumber起码得几百刀了,而且要多少你都得给。我不知道叫自来水公司要不要钱,这次我是取消了,不然说不定事后给张账单也说不准。

在Java里怎样把exception的stacktrace压缩到一行?

在Java里怎样把exception的stacktrace压缩到一行?

一般在Java里,我们可以用printStackTrace把stacktrace打印出来。

try {
    // do something
} catch (Exception e) {
    e.printStackTrace();    
}

在server里,如果我们需要用graylog来search log,就需要把stacktrace压缩到一行。我们可以用apache的ExceptionUtils

import org.apache.commons.lang.exception.ExceptionUtils;
//...

try {
    // do something
} catch (Exception e) {
    logger.error("Exception: {}", 
        ExceptionUtils.getStackTrace(e)
    );
}

Macbook 中文输入法使用全角/半角标点符号

Macbook 中文输入法使用全角/半角标点符号

在我的Macbook上写中文,标点符号都是半角的(也就是英文的)。一直都很疑惑,本来还以为是wordpress的问题。今天好奇在网上做了一下研究,终于找到原因了。

解决方法

点击屏幕上方的菜单栏的输入法

注意下拉菜单里面有个“Use Halfwidth Punctuation“的选项,如果这个是打勾的,就会用半角标点符号。再点击一下去掉勾就可以变会正常的全角中文标点符号了。

怎样在Excel里正确显示韩语

怎樣在Excel裡正確顯示韓語

最近遇到一个问题,用Excel打开含有韩语CSV文件, 里面的韩语字符都是乱码。后来发现问题是那些CSV的encoding格式是UTF-8。如果直接用Excel打开,解码格式就不对。

解决方法

在File目录选import

选CSV

选择文件,然后重要的一步, 选择File origin,然后选UTF-8

剩下的正常操作, 选择delimiter,这样韩语(其实是任何 UTF-8的字符)就可以在Excel正常显示了。

ECharts快速上手

ECharts快速上手

前段一直是我的短板, 没有太多机会系统学习. 最近想做一个股票分析系统的webapp, 于是就在网上找合适的javascript library做data visualization. D3.js以前用过, 觉得太过底层, 写起来太麻烦. 于是发现了ECharts, 好像是国内大神写得, 已经是Apache project, 非常厉害. 于是follow了这个tutorial. 里面有些小细节我觉得初学者可能会卡住, 这里我写得详细一点.

安装npm

npm是node package manager的意思, 是用了管理安装javascript libraries的. 相当于Python里的pip. 安装可以看我前面的文章.

安装ECharts

首先给项目建立一个新的directory

% mkdir test_echart
% cd test_echart

在项目directory里安装echarts library

npm install echarts --save

完成后directory里会有那么几个东西

% ls
package-lock.json
node_modules/		package.json

开始Coding

在项目directory里面添加一个html file叫main.html, 内容如下调用echart

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <!-- including ECharts file -->
    <script src="node_modules/echarts/dist/echarts.min.js"></script>
</head>

<body>
    <!-- preparing a DOM with width and height for ECharts -->
    <div id="main" style="width:600px; height:400px;"></div>
</body>

<script type="text/javascript">
        // based on prepared DOM, initialize echarts instance
        var myChart = echarts.init(document.getElementById('main'));

        // specify chart configuration item and data
        var option = {
            title: {
                text: 'ECharts entry example'
            },
            tooltip: {},
            legend: {
                data:['Sales']
            },
            xAxis: {
                data: ["shirt","cardign","chiffon shirt","pants","heels","socks"]
            },
            yAxis: {},
            series: [{
                name: 'Sales',
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20]
            }]
        };

        // use configuration item and data specified to show chart
        myChart.setOption(option);
    </script>
</html>

header里面的这行相当于Python的import

    <script src="node_modules/echarts/dist/echarts.min.js"></script>

现在directory内容如下

% ls
main.html		package-lock.json
node_modules/		package.json

用浏览器打开main.html. 在Mac里可以在terminal里用

% open main.html

网页显示如下, 还是挺酷的.

简单看看js code, option就是一个json object, 里面包含了图的一些信息和data. 一开始init了一个echarts object, 然后给它set一下option这个json object. 和d3比起来还是简单多了.

        var myChart = echarts.init(document.getElementById('main'));
        
        var option = {
            // json object
        };

        myChart.setOption(option);

我把数据改一改, 就可以显示一些投资标的的市值了

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <!-- including ECharts file -->
    <script src="node_modules/echarts/dist/echarts.min.js"></script>
</head>

<body>
    <!-- preparing a DOM with width and height for ECharts -->
    <div id="main" style="width:1000px; height:400px;"></div>
</body>

<script type="text/javascript">
        // based on prepared DOM, initialize echarts instance
        var myChart = echarts.init(document.getElementById('main'));

        // specify chart configuration item and data
        var option = {
            title: {
                text: 'Market Caps'
            },
            tooltip: {},
            legend: {
                data:['Sales']
            },
            xAxis: {
                data: []
            },
            yAxis: {},
            series: [{
                name: 'Sales',
                type: 'bar',
                data: []
            }]
        };

        const MARKET_CAP = 'market cap';
        var legends = [MARKET_CAP];
        var symbols = ['AAPL', 'GOOGL', 'FB', 'AMZN', 'NVDA', 'NFLX', 'TSLA', 'BTC', 'ETH', 'V', 'MA']
        var marketCaps = [2256, 1185, 778, 1634, 323, 238, 669, 622, 109, 482, 356];

        option.legend.data = legends;
        option.xAxis.data = symbols;
        option.series[0].data = marketCaps;	
        option.series[0].name = MARKET_CAP;	

        // use configuration item and data specified to show chart
        myChart.setOption(option);
    </script>
</html>

网页更新如下. 接上个数据的后端就可以显示不同的数据了.

Python/Pandas实战: 处理IBKR Statement

Python/Pandas实战: 处理盈透Statement

Pandas是一个非常强大的数据分析方面的Python package. 如果是做Machine Learning或者数据分析, 掌握Pandas很省去很多麻烦. 许多Machine Learning前期的数据处理也是用Pandas做得.

IBKR(Interactive Brokers, 有时简称IB, 中文叫盈透证券)是美国老牌券商, 也是我的主要使用的券商. 又到了辞旧迎新的时候, 需要看看2020投资收益, 于是趁新年长周末写点小程序做点数据分析. 而这正好覆盖了Pandas的各种常用functions.

下载IB Statement

IB Statement提供多种方式下载, 比如html, pdf, csv. 用作数据处理选csv. 内容大概长这样

IB statement例子

Statement这个column是内容, 里面有很多项, 而这里我只看“Realized & Unrealized Performance Summary”, 然后相同的第一列后面的列数都是一样的. 上图是Jupyter Lab的显示有问题. 因为文件其实可以看成很多CSV files连在一起, 而第一列可以看成是小csv的文件名. 然后第二列是Header或者Data. Header那行就是告诉你后面的Data行里每一列都是什么. 于是我们可以把数据读到内存里.

建立Pandas DataFrame

import pandas as pd

field = 'Realized & Unrealized Performance Summary'

f = open('statement.csv', 'r')

rows = []
for line in f:
    cols = line.strip().split(',')
    if cols[0] == field:
        if cols[1] == 'Header':
            header_row = cols[2:]
        else:
            rows.append(cols[2:])

header_ros是个list of string

['Asset Category',
 'Symbol',
 'Cost Adj.',
 'Realized S/T Profit',
 'Realized S/T Loss',
 'Realized L/T Profit',
 'Realized L/T Loss',
 'Realized Total',
 'Unrealized S/T Profit',
 'Unrealized S/T Loss',
 'Unrealized L/T Profit',
 'Unrealized L/T Loss',
 'Unrealized Total',
 'Total',
 'Code']

而rows是list of list. 就是list of rows

[['Stocks',
  'AAPL',
  '0',
  '0',
  '0',
  '0',
  '0',
  '0',
  '29564.510608',
  '-14.176325',
  '0',
  '0',
  '29550.334283',
  '29550.334283',
  '\n'],
 ['Stocks',
  'AMD',
  '0',
  '0',
  '0',
  '0',
  '0',
  '0',
  '220.208447',
  '0',
  '0',
  '0',
  '220.208447',
  '220.208447',
  '\n'],
...
]

接下来就可以用Panda create dataframe了

df = pd.DataFrame(rows, columns = header_row)
df.head(5)

这里有个问题. Created出来的dataframe的数据类型有数字有string, 全部都是object. 如果sort by数字column得到的结果是sort by string, 并不是我们想要的. 可以用dtypes来确定

df.dtypes
Asset Category           object
Symbol                   object
Cost Adj.                object
Realized S/T Profit      object
Realized S/T Loss        object
Realized L/T Profit      object
Realized L/T Loss        object
Realized Total           object
Unrealized S/T Profit    object
Unrealized S/T Loss      object
Unrealized L/T Profit    object
Unrealized L/T Loss      object
Unrealized Total         object
Total                    object
Code                     object
dtype: object

这里我把需要转换成数字的columns转换了

header_text_col = set(['Asset Category', 'Symbol', 'Code'])
num_col = list(set(header_row) - header_text_col)
df[num_col] = df[num_col].apply(pd.to_numeric)
df.dtypes
Asset Category            object
Symbol                    object
Cost Adj.                  int64
Realized S/T Profit      float64
Realized S/T Loss        float64
Realized L/T Profit        int64
Realized L/T Loss        float64
Realized Total           float64
Unrealized S/T Profit    float64
Unrealized S/T Loss      float64
Unrealized L/T Profit      int64
Unrealized L/T Loss      float64
Unrealized Total         float64
Total                    float64
Code                      object
dtype: object

这样就可以对数字类型的column进行排序了, 比如下面指令做2步操作
1. 选出Asset Category是”Stocks”的rows, 相当于SQL里的where, 语法为df[df['Asset Category'] == 'Stock'], return是另一个filtered好的dataframe
2. 按Realized Total从小到大排序, return也是另一排序好的dataframe

df[df['Asset Category'] == 'Stock'].sort_values(by=['Realized Total'], ascending=True)

还可以画图

stock_gain = df[
    (df['Asset Category'] == 'Stocks') & (
        df['Realized Total'] != 0.0)][['Symbol', 'Realized Total']].sort_values(
    by=['Realized Total'], ascending=True)
stock_gain

还可以画图

stock_gain.plot.bar(x='Symbol')

技术小结

建立dataframe

rows是list of rows(lists), header_row是list of string

df = pd.DataFrame(rows, columns = header_row)

显示前n行

df.head(10)

显示后面n行

df.tail(10)

把特定columns转换成数字(numerical type)

num_col是list of strings, 下面命令会把num_col里的columns都转化成数字类. 类似SQL里的select cast(col1 as double), cast(col2 as double), ... from df

df[num_col] = df[num_col].apply(pd.to_numeric)

Filter和sort

类似SQL里, select * from df where asset_category = 'Stocks' order by realized_total asc;

df[df['Asset Category'] == 'Stocks'].sort_values(by=['Realized Total'], ascending=True)

在Apache2里怎样redirect URL? | How to redirect URL using Apache2?

在Apache2里怎样redirect URL?

原来以为这个问题很简单, Google了一下发现网上的答案五花八门, 而且版本很混乱 有httpd的, ASP.net, url redirect的, url rewrite的. 经过多次尝试各种组合, 终于试出了一个可行的解决方案, 值得在此记录一下, 相信一定能帮到有类似需求的人. 本文例子为Ubuntu + apache2, 2020年, 2021年左右.

普通URL redirect到另一个网站

编辑/etc/apache2/sites-available里面的一个site, 比如mysite, 需要redirect到anothersite.com, 那么可以加上一行Redirect

<VirtualHost *:80>
ServerAdmin webmaster@localhost
...
         ServerName mysite.com
         ServerAlias www.mysite.com
         DocumentRoot /var/www/html/mysite
         Redirect / http://anothersite.com
...         

修改完成后保存, 重启apache2

sudo service apache2 restart

Redirect到一个子目录

上面的方法可以redirect到不同的URL, 但是如果想redirect mysite.com到mysite.com/blog就不行. 我试了一下, 如果像上面那样改动, 在浏览器输入mysite.com就会被不停的改成mysite.com/blog/blog/blog… 无限循环. 很明显, 改写是recursive的. 那么就不要用Redirect而是用RedirectMatch

<VirtualHost *:80>
ServerAdmin webmaster@localhost
...
         ServerName mysite.com
         ServerAlias www.mysite.com
         DocumentRoot /var/www/html/mysite
         RedirectMatch ^/$ /blog/
...       

我也不太清楚apache2配置的语法细节, 但是以上是试过可行的方法. 最后一样是重启apache2让配置生效

sudo service apache2 restart

2020-12-26 比特币定投实验更新 | 2020-12-26 Update on Dollar average plan for buying bitcoin

2020-12-26 比特币定投实验更新

牛年还没到, 最近比特币就已经气冲天, 今天单价已经过了$26,000. 因为Concoinbase Pro有一次交易最少要买0.001个比特币的限制, 入场门槛越来越高了, 现在最少一次也要买入$26.xx以上了. 今天以前总投入$60, holding总价值$86.xx, 不到一个月总回报43%! 如果把全部资产买入真的是有机会改变阶级. 不过能赚到不正是因为赌注下少, 不在乎它的短期波动, 所以才能拿的住吗?我觉得还是继续执行定投计划, 也许会适量增加一点投入, 但注码量要控制在即使输光也不会让自己难受的范围内, 毕竟这还是有可能归零的高风险投资, 而自己对比特币还并不是很熟悉, 有时间的时候还是要多学习, 或者能做为程序员参与到相关项目开发中, 这样才有信心下比较重的注码投入. 不然就还是以娱乐为主吧.

今天新投入$30, 买入0.00111758, 均价$26,710.00. 总投入$90, holding现价值116.10.