SegmentFault 欧阳松的博客最新的文章
2018-07-21T21:49:48+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
Pandas 学习笔记
https://segmentfault.com/a/1190000015719037
2018-07-21T21:49:48+08:00
2018-07-21T21:49:48+08:00
ouyangsong
https://segmentfault.com/u/ouyangsong
0
<p>Pandas 的基本概念就是 DataFrame,所有属性和操作都是围绕它而来。</p>
<p><!--more--></p>
<p>Padans 中的每一列叫做 Series,每一个 Series 中的数据类型要保持一致,但是 DataFrame 中的 Series 的类型可以不一样。</p>
<p>一般 Python 三剑客的导入的方法如下:</p>
<pre><code class="python">import numpy as np
import pandas as pd
import matplotlib.pyplot as plt</code></pre>
<h2>创建 Series 和索引</h2>
<p>序列 = 数据 + 索引 + 序列名 + 数据类型</p>
<pre><code class="python">>>> s_age = pd.Series(data=[1, 2, 3, 4], index=["a", "b", "c", "d"], name="mySeries", dtype=np.int32)
>>> s_age
a 1
b 2
c 3
d 4
Name: mySeries, dtype: int32
>>> s_age.reindex(['a','b','e']) # 重排
a 1.0
b 2.0
e NaN
Name: mySeries, dtype: float64
>>> s_age[0]
1
>>> s_age[1:2]
b 2
Name: mySeries, dtype: int32
>>> s_age[1:2]=22
>>> s_age
a 1
b 22
c 3
d 4
Name: mySeries, dtype: int32
>>> s_age["a"]
1</code></pre>
<h2>创建 DataFrame</h2>
<h3>用 Series 创建</h3>
<pre><code class="python">>>> s1 = pd.Series(data=["M", "F", "M", "F"], index=["a", "b", "c", "d"], name="sex")
>>> s2 = pd.Series(data=[21, 22, 23, 24], index=["a", "b", "c", "d"], name="age")
>>> df = pd.DataFrame({'sex': s1.astype("category"), 'age': s2})
>>> df
age sex
a 21 M
b 22 F
c 23 M
d 24 F</code></pre>
<h3>从文件中读取</h3>
<p>包括但不限于 csv,数据库。</p>
<pre><code class="python">iris=pd.read_csv('https://raw.github.com/pydata/pandas/master/pandas/tests/data/iris.csv', sep=',')</code></pre>
<h2>查看属性</h2>
<pre><code class="python">>>> df.index # 行名
Index(['a', 'b', 'c', 'd'], dtype='object')
>>> df.columns # 列名
Index(['age', 'sex'], dtype='object')
>>> df.dtypes # 列属性,也就是 Series 类型
age int64
sex category
dtype: object</code></pre>
<p>head 和 tail 就和 Linux 下面的 haed 和 tail 命令类似。</p>
<pre><code class="python">>>> df.head()
age sex
a 21 M
b 22 F
c 23 M
d 24 F
>>> df.tail()
age sex
a 21 M
b 22 F
c 23 M
d 24 F
>>> df.describe()
age
count 4.000000
mean 22.500000
std 1.290994
min 21.000000
25% 21.750000
50% 22.500000
75% 23.250000
max 24.000000</code></pre>
<h2>聚合</h2>
<p>就和 sql 中的 groupby 类似。</p>
<pre><code class="python">>>> df.groupby([df["sex"]]).agg({"age": ["sum", "mean"]})
age
sum mean
sex
F 46 23
M 44 22</code></pre>
<h2>排序</h2>
<p>可以指定排序的依据,是否倒序等等。</p>
<pre><code class="python">>>> df.sort_index(ascending=False)
age sex
d 24 F
c 23 M
b 22 F
a 21 M
>>> df.sort_values(by="age", ascending=False)
age sex
d 24 F
c 23 M
b 22 F
a 21 M</code></pre>
<h2>透视</h2>
<pre><code class="python">>>> df
age sex sorce
a 21 M 11
b 22 F 12
c 23 M 13
d 24 F 14
>>> df.pivot_table(index=['age'], columns=['sex'], values=['sorce'], aggfunc=[len, np.mean,np.sum],margins=True, fill_value=0)
len mean sum
sorce sorce sorce
sex F M All F M All F M All
age
21 0 1 1 0 11 11 0 11 11
22 1 0 1 12 0 12 12 0 12
23 0 1 1 0 13 13 0 13 13
24 1 0 1 14 0 14 14 0 14
All 2 2 4 13 12 12 26 24 50</code></pre>
<h2>索引</h2>
<p>也就是定位元素。</p>
<pre><code class="python">>>> df[1:3] # 切片
age sex sorce
b 22 F 12
c 23 M 13
>>> df[df.sorce>12] # bool 索引
age sex sorce
c 23 M 13
d 24 F 14
>>> df.query('sorce>12') # sql 索引
age sex sorce
c 23 M 13
d 24 F 14
>>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
>>> df.where(df.A>2, 10) # 不满足条件的填充
A B
0 10 10
1 10 10
2 4 5
3 6 7
4 8 9
>>> df
A B
a 0 1
b 2 3
c 4 5
d 6 7
e 8 9
>>> df.A # 选择列
a 0
b 2
c 4
d 6
e 8
Name: A, dtype: int64
>>> df[['A', 'B']] # 选择多列
A B
a 0 1
b 2 3
c 4 5
d 6 7
e 8 9
>>> df.loc[['a','b'], ['A','B']] # 选择行与列
A B
a 0 1
b 2 3
>>> df.iloc[1:3, 0:2] # 根据行列下标来选择
A B
b 2 3
c 4 5</code></pre>
<h2>合并数据</h2>
<pre><code class="python"># axis 设置纬度,join 指定合并方式,参考 sql 的连接
pd.concat([df1,df2],axis=0,join='outer')
# merge 就和 sql 的 join 类似
pd.merge(df1,df2,on=['age'],how='left')</code></pre>
<p>更多参数和直观的体现可以参考 <a href="https://link.segmentfault.com/?enc=UgSf0%2FEd7gkaKDbSy%2FZC%2BQ%3D%3D.Iy1%2BxE6Qw2E91lPKwNGqx2NTXWBvKG9K4ouHyyTO6pMc6Gme9gPLnOrhyvB3hLY3l829hBuQvnrqdoNnxqu3VFwa0KssXYW7Qcomo3rT9NmI2tRVj%2BPEvlqKNPopLUmscP%2Bu%2BcsvNQT%2BbOkEy78d0A%3D%3D" rel="nofollow">Merge, join, and concatenate</a>。</p>
<h2>采样</h2>
<pre><code class="python">df1.sample(n=100, weights='age',axis=0, replace=True)</code></pre>
<p>sklearn 也有对 DataFrame 进行 shuffle 的函数。</p>
<p><a href="https://link.segmentfault.com/?enc=w94S6bcbDVmlBZvhwRuaVQ%3D%3D.sddLsk4H9l8VUipQrlnac4l6FfgEaZuPXXPuGXTCClCdDUVKTTo6HNYKpKqXQ7XG" rel="nofollow">https://www.ouyangsong.com/po...</a></p>
Numpy 学习笔记
https://segmentfault.com/a/1190000015719020
2018-07-21T21:48:06+08:00
2018-07-21T21:48:06+08:00
ouyangsong
https://segmentfault.com/u/ouyangsong
0
<p>Numpy 的基本概念就是 ndarray 数组,所有属性和操作都是围绕它而来。</p>
<p><!--more--></p>
<h2>开始使用</h2>
<p>一般公认的导入方法如下:</p>
<pre><code class="python">import numpy as np</code></pre>
<h2>常用属性</h2>
<pre><code class="python">>>> a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float64)
>>> a.ndim # 维度
2
>>> a.shape # 数组形状
(2, 3)
>>> a.size # 元素个数
6
>>> a.dtype # 元素类型
dtype('float64')
>>> a.itemsize # 元素字节数
8
>>> a.T # 转置
array([[ 1., 4.],
[ 2., 5.],
[ 3., 6.]])
>>> a.flat # 返回 numpy.flatiter 对象,可迭代
<numpy.flatiter object at 0x7f97d9811800></code></pre>
<h2>创建</h2>
<h3>从 list 或者 tuple</h3>
<pre><code class="python">>>> a = np.array([[1, 2, 3], [4, 5, 6]])</code></pre>
<h3>直接生成</h3>
<p>指定 shape 和 dtype 生成。zeros 代表全是 0,ones 代表全是 1,empty 代表不初始化。</p>
<pre><code class="python">>>> a = np.zeros(shape=(2, 3), dtype=np.float64)
>>> a
array([[ 0., 0., 0.],
[ 0., 0., 0.]])
>>> a = np.ones(shape=(2, 3), dtype=np.float64)
>>> a
array([[ 1., 1., 1.],
[ 1., 1., 1.]])</code></pre>
<p>复制别的 ndarry 的 shape 和 dtype 来创建。类似的,zeros_like 代表全部初始化为 0,ones_like 代表全部初始化为 1,empty_like 不初始化。</p>
<pre><code class="python">>>> b = np.zeros_like(a)
>>> a
array([[ 1., 1., 1.],
[ 1., 1., 1.]])</code></pre>
<p>指定 shape 来用随机数初始化。</p>
<pre><code class="python">>>> np.random.rand(2,3) # 0~1 均匀分布
array([[ 0.31119951, 0.84391927, 0.1113187 ],
[ 0.30782159, 0.67313418, 0.46963388]])
>>> np.random.randn(2,3) # 标准正态分布
array([[-2.25811477, -0.34537208, 1.34830224],
[-0.97916617, -1.17038121, -0.51444696]])</code></pre>
<p>用序列,也就是起点,终点和步长,类似切片。</p>
<pre><code class="python">>>> np.arange(start=1, stop=7, step=1, dtype=np.float64).reshape(2,3)
array([[ 1., 2., 3.],
[ 4., 5., 6.]])
>>> np.linspace(start=1, stop=4, num=6, endpoint=True, dtype=np.float64) # 均分
array([ 1. , 1.6, 2.2, 2.8, 3.4, 4. ])
>>> np.logspace(start=1, stop=4, num=6, endpoint=True, dtype=np.float64, base=10.0) # 等比数列
array([ 10. , 39.81071706, 158.48931925, 630.95734448,
2511.88643151, 10000. ])</code></pre>
<p>使用生成函数。</p>
<pre><code class="python">>>> np.fromfunction(lambda i, j:i+j, (2, 3), dtype=np.float64)
array([[ 0., 1., 2.],
[ 1., 2., 3.]])</code></pre>
<h3>从文件读取</h3>
<pre><code class="python">>>> np.save("a.npy", a)
>>> b = np.load("a.npy")
>>> b
array([[ 1., 1., 1.],
[ 1., 1., 1.]])</code></pre>
<h2>基本操作</h2>
<h3>查</h3>
<p>直接用位置或者通过条件来索引。</p>
<pre><code class="python">>>> a
array([[ 1., 2., 3.],
[-4., -5., -6.]])
>>> a[1,1]
-5.0
>>> a[1, 0:1]
array([-4.])
>>> a[1, :]
array([-4., -5., -6.])
>>> a[1, ...] # 除了第一个纬度是 1,其他纬度都可以
array([-4., -5., -6.])
>>> a[[1, 0], :]
array([[-4., -5., -6.],
[ 1., 2., 3.]])
>>> a[a<0] # bool 索引
array([-4., -5., -6.])</code></pre>
<h3>改</h3>
<p>对上面索引到的位置赋值。</p>
<pre><code class="python">>>> a
array([[ 1., 2., 3.],
[-1., -1., -1.]])
>>> a.flat[1:2] = 5
>>> a
array([[ 1., 5., 3.],
[-1., -1., -1.]])</code></pre>
<h3>拼接</h3>
<pre><code class="python">>>> b
array([[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> a
array([[ 1., 5., 3.],
[-1., -1., -1.]])
>>> np.concatenate([a, b], axis=0) # 沿垂直方向拼接
array([[ 1., 5., 3.],
[-1., -1., -1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> np.concatenate([a, b], axis=1) # 沿水平方向拼接
array([[ 1., 5., 3., 1., 1., 1.],
[-1., -1., -1., 1., 1., 1.]])</code></pre>
<p><code>np.vstack([x,y])</code>,<code>np.hstack([x,y])</code>,<code>np.dstack([x,y])</code> 分别对应 axis 等于 0,1,2。</p>
<h3>分割</h3>
<p>指定索引编号分割。</p>
<pre><code class="python">np.array_split(a, [1,3], axis=0)</code></pre>
<p>指定纬度,均等分。<code>np.vsplit()</code>,<code>np.hsplit()</code>,<code>np.dsplit()</code>。</p>
<h3>复制</h3>
<p>分成浅度复制和深度复制,区别就是有没有独立的内存。</p>
<pre><code class="python">b = a.view() # 浅度复制
b = a.copy() # 深度复制</code></pre>
<h2>计算</h2>
<p>这里主要掌握广播,所谓广播就是要么相同形状,要么某一个维度上相同,要么只有一个数字扩展成一个维度。</p>
<pre><code class="python">>>> a
array([[ 2., 10., 6.],
[ -2., 12., -2.]])
>>> a*a
array([[ 4., 100., 36.],
[ 4., 144., 4.]])
>>> a+1
array([[ 3., 11., 7.],
[ -1., 13., -1.]])
>>> a*=2
>>> a
array([[ 4., 20., 12.],
[ -4., 24., -4.]])</code></pre>
<p>利用内置函数来广播。</p>
<pre><code class="python">>>> a
array([[ 4., 20., 12.],
[ -4., 24., -4.]])
>>> np.exp(a)
array([[ 5.45981500e+01, 4.85165195e+08, 1.62754791e+05],
[ 1.83156389e-02, 2.64891221e+10, 1.83156389e-02]])
>>> np.max(a)
24.0</code></pre>
<p>线性函数相关。</p>
<pre><code class="python">>>> a
array([[ 4., 20., 12.],
[ -4., 24., -4.]])
>>> b = b.T
>>> b
array([[ 1., -1.],
[ 5., 7.],
[ 3., -1.]])
>>> a.dot(b)
array([[ 140., 124.],
[ 104., 176.]])</code></pre>
<p><a href="https://link.segmentfault.com/?enc=9RI6eTyWndvX7bDzpugCkA%3D%3D.ZKTsEKO4PtTFO%2BpFTGTQCX5fVCC%2BMbbljlaj4mYsZ7UK369oqi18WA8kfxkSO6rU" rel="nofollow">https://www.ouyangsong.com/po...</a></p>
Morris 遍历二叉树
https://segmentfault.com/a/1190000015719013
2018-07-21T21:45:43+08:00
2018-07-21T21:45:43+08:00
ouyangsong
https://segmentfault.com/u/ouyangsong
2
<p>Morris Traversal 方法实现前序、中序以及后序遍历二叉树。相比使用栈或者递归(也是通过栈空间)方法,Morris 方法可以在空间复杂度为 <code>O(1)</code>,时间复杂度为 <code>O(n)</code> 的条件下实现对二叉树的遍历。</p>
<p><!--more--></p>
<h2>前序遍历</h2>
<p><img src="/img/remote/1460000015719016" alt="Binary-Tree-Preorder-Traversal" title="Binary-Tree-Preorder-Traversal"></p>
<ol>
<li>如果当前节点左孩子 cur->left 为空,输出当前节点 cur 并指向右孩子 cur->right。</li>
<li>
<p>如果当前节点左孩子 cur->left 不为空,那么在当前节点的左子树中找出前驱节点 pre,也就是左子树中最大的点。</p>
<ul>
<li>如果前驱节点的右孩子 pre->right 为空,那么将右孩子指向当前节点。<strong>输出当前节点</strong>。当前节点更新为当前节点的左孩子。</li>
<li>如果前驱节点的右孩子 pre->right 不为空,也就是指向当前节点。重新将右孩子设为空。当前节点更新为当前节点的右孩子。</li>
</ul>
</li>
<li>重复 1、2 直到当前节点为空。</li>
</ol>
<pre><code class="cpp">class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
TreeNode* cur = root;
TreeNode* pre = NULL;
vector<int> result;
while(cur!=NULL){
if (cur->left == NULL){
result.push_back(cur->val);
cur = cur->right;
}else{
pre = cur->left;
while(pre->right!=NULL && pre->right!=cur){
pre = pre->right;
}
if (pre->right==NULL){
pre->right=cur;
result.push_back(cur->val);
cur = cur->left;
}else{
pre->right = NULL;
cur = cur->right;
}
}
}
return result;
}
};</code></pre>
<h2>中序遍历</h2>
<p><img src="/img/remote/1460000015719017" alt="Binary-Tree-Inorder-Traversal" title="Binary-Tree-Inorder-Traversal"></p>
<ol>
<li>如果当前节点左孩子 cur->left 为空,输出当前节点 cur 并指向右孩子 cur->right。</li>
<li>
<p>如果当前节点左孩子 cur->left 不为空,那么在当前节点的左子树中找出前驱节点 pre,也就是左子树中最大的点。</p>
<ul>
<li>如果前驱节点的右孩子 pre->right 为空,那么将右孩子指向当前节点。当前节点更新为当前节点的左孩子。</li>
<li>如果前驱节点的右孩子 pre->right 不为空,也就是指向当前节点。重新将右孩子设为空。<strong>输出当前节点</strong>。当前节点更新为当前节点的右孩子。</li>
</ul>
</li>
<li>重复 1、2 直到当前节点为空。</li>
</ol>
<pre><code class="cpp">class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
TreeNode* cur = root;
TreeNode* pre = NULL;
vector<int> result;
while(cur!=NULL){
if (cur->left == NULL){
result.push_back(cur->val);
cur = cur->right;
}else{
pre = cur->left;
while(pre->right!=NULL && pre->right!=cur){
pre = pre->right;
}
if (pre->right == NULL){
pre->right=cur;
cur = cur->left;
}else{
pre->right = NULL;
result.push_back(cur->val);
cur = cur->right;
}
}
}
return result;
}
};</code></pre>
<h2>后序遍历</h2>
<p><img src="/img/remote/1460000015719018" alt="Binary-Tree-Postorder-Traversal" title="Binary-Tree-Postorder-Traversal"></p>
<ol>
<li>新增临时节点 dump,并且将 root 设为 dump 的左孩子。</li>
<li>如果当前节点左孩子 cur->left 为空,输出当前节点 cur 并指向右孩子 cur->right。</li>
<li>
<p>如果当前节点左孩子 cur->left 不为空,那么在当前节点的左子树中找出前驱节点 pre,也就是左子树中最大的点。</p>
<ul>
<li>如果前驱节点的右孩子 pre->right 为空,那么将右孩子指向当前节点。当前节点更新为当前节点的左孩子。</li>
<li>如果前驱节点的右孩子 pre->right 不为空,也就是指向当前节点。<strong>逆序输出当前节点左孩子到前序节点的路径</strong>。重新将右孩子设为空。当前节点更新为当前节点的右孩子。</li>
</ul>
</li>
<li>重复 2、3 直到当前节点为空。</li>
</ol>
<p>逆序打印路径其实就是逆序打印单链表节点。先将单链表反转,然后依次打印,接下来重新反转到初始状态。</p>
<pre><code class="cpp">class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
TreeNode dump(-1);
dump.left = root;
TreeNode* cur = &dump;
TreeNode* pre = NULL;
vector<int> result;
while(cur!=NULL){
if (cur->left == NULL){
cur = cur->right;
}else{
pre = cur->left;
while(pre->right!=NULL && pre->right!=cur){
pre = pre->right;
}
if (pre->right == NULL){
pre->right=cur;
cur = cur->left;
}else{
printReverse(cur->left, pre, result);
pre->right = NULL;
cur = cur->right;
}
}
}
return result;
}
void printReverse(TreeNode* from, TreeNode* to, vector<int>& result){
Reverse(from, to);
TreeNode* p = to;
while(true){
result.push_back(p->val);
if(p == from){
break;
}
p = p->right;
}
Reverse(to, from);
}
void Reverse(TreeNode* from, TreeNode* to){
TreeNode* x = from;
TreeNode* y = from->right;
TreeNode* z;
if (from == to){
return;
}
x->right = NULL;
while(x != to){
z = y->right;
y->right = x;
x = y;
y = z;
}
}
};</code></pre>
<h2>总结</h2>
<p>Morris 方法遍历之所以能够在 <code>O(1)</code> 的空间的条件下完成是因为它充分利用到叶子的左右孩子来记录上层关系,从而不需要额外的栈空间来记录顺序关系。通过三种遍历可以看到,其实总体上的代码逻辑没有发生改变,主要是改变了输出结果的时机和方式。</p>
<p><a href="https://link.segmentfault.com/?enc=e6bap1t%2BVKXkAH9A6y0QjQ%3D%3D.ueOugf%2BVPXlzg3%2FgpWL0Z34QQIJAlikbvuVjILagxgvQCfZr63QLpYWLi1CDrY64" rel="nofollow">https://www.ouyangsong.com/po...</a></p>
SSH 使用小技巧
https://segmentfault.com/a/1190000013967598
2018-03-25T10:52:44+08:00
2018-03-25T10:52:44+08:00
ouyangsong
https://segmentfault.com/u/ouyangsong
2
<p>SSH 的端口转发功能可以加密 Client 和 Server之间的通讯数据,还可以突破防火墙的限制。北邮的电脑上网,ipv4 地址是每次连接网关时动态分配,ipv6 地址是不会变的。可以利用端口转发来连接实验室机器,无视它的 ip 每次都变化。</p>
<h2>基本参数含义</h2>
<table>
<thead><tr>
<th>参数</th>
<th>功能</th>
</tr></thead>
<tbody>
<tr>
<td>-C</td>
<td>压缩数据传输</td>
</tr>
<tr>
<td>-f</td>
<td>后台运行</td>
</tr>
<tr>
<td>-N</td>
<td>不执行shell</td>
</tr>
<tr>
<td>-L</td>
<td>本地端口转发</td>
</tr>
<tr>
<td>-R</td>
<td>远程端口转发</td>
</tr>
<tr>
<td>-D</td>
<td>动态端口转发</td>
</tr>
</tbody>
</table>
<h2>远程端口转发</h2>
<p>远程端口可以把本地的端口转发到远程机器上,实现反向端口转发</p>
<h3>登陆端口转发</h3>
<p>我们面对的是这样的场景:实验室有一台机器,然后希望可以通过 SSH 连接上去跑机器学习实验,但是由于学校网络问题,实验室的机器的 ip 经常发生改变,而且因为该机器没有公网 ip ,所以只能校园网内通过局域网 ip 连接。 </p>
<p>解决这个问题,我们首先需要一台公网机器,比如腾讯云或者阿里云,要求它有一个公网 ip ,然后实验室机器和客户端机器都可以连接到公网机器。下面为了便于表述,我将实验室的机器称为 lab,公网机器称为 jump,连接的客户端为 client 。为了实验室机器能够开机就能连接公网机器,可以参考 <a href="https://link.segmentfault.com/?enc=HItHxvdDobp8Tgc2yCWU8Q%3D%3D.WKhdM55%2Feu56Plfie2BDNWXBIpUc5QE4p48ILekXVMHYWXm1sVACqwe8SSoyMKBH" rel="nofollow">命令行登陆校园网</a> 来自动登陆网关。我们在 lab 机器上有一个用户为 ouyangsong,在 jump 机器上有个用户为 jumpuser 。</p>
<p><img src="/img/remote/1460000013974071" alt="" title=""></p>
<p>为了方便后续的登陆,首先需要建立 ouyangsong 到 jumpuser 的免密码登陆,也就是说 lab 机器可以不需要密码就可以登陆到 jump 机器。具体可以参考 <a href="https://link.segmentfault.com/?enc=CKVxw%2FamBuF%2BHMUeh%2FSayA%3D%3D.0xhHjojfC%2BGEet9tplQFTBldB65%2Bjuw1XiajWiMMSvzVkGd7hX3y21q68lQJ%2B3HT" rel="nofollow">SSH 免密码登陆</a> 。</p>
<pre><code class="shell">lab$ ssh -R 10022:localhost:22 jumpuser@jump</code></pre>
<p>R 参数接受三个值,分别是“远程主机端口:目标主机:目标主机端口” 。这条命令的意思就是,让 jump 机器监听它的 10022 端口,然后所有数据经过 lab 转发到 lab 的 22 端口。这时候我们可以在 jump 机器上通过 <code>ss -ant</code> 命令看到它已经监听本地(指 jump 机器本地)的 10022 端口。</p>
<pre><code class="shell">jump$ ssh ouyangsong@lab -p10022
lab$</code></pre>
<p>这样的话首先登陆到 jump 机器,然后输入 ouyangsong 在 lab 机器上的密码就可以登陆到 lab 机器。但是这样还是略显麻烦,可以把 jump 机器的 ssh 端口开放在 <code>0.0.0.0</code> 而不是默认的 <code>127.0.0.1</code> 就可以直接在 client 端登陆到 lab 机器。修改 <code>/etc/ssh/sshd_config</code> 文件,添加 <code>GatewayPorts yes</code> 。然后重启 sshd 服务,<code>sudo reload ssh</code>。这时候还需要把远程端口转发命令做点小修改。</p>
<pre><code class="shell">lab$ ssh -R 0.0.0.0:10022:localhost:22 jumpuser@jump</code></pre>
<p>经过上面的修改我们可以在 client 上执行如下命令就可以登陆 lab 机器了,成功突破了校园网的限制。注意用户名为 lab 上的用户名,jump 机器不存在 ouyangsong 用户。</p>
<pre><code class="shell">client$ ssh ouyangsong@jump -p10022</code></pre>
<p>但是还有点小问题就是如果网络不稳定,那么 ssh 服务就会断了,这时候我们需要重新连接 ssh ,可以使用 autossh 工具。</p>
<pre><code class="shell">sudo apt install autossh
# M 参数指定监视连接状态端口
autossh -M 10023 -fNR 0.0.0.0:10022:localhost:22 jumpuser@jump</code></pre>
<p>然后添加到开机自启中,<code>/etc/rc.local</code> 最后添加下面命令:</p>
<pre><code class="shell">su - ouyangsong -c 'autossh -M 10023 -fNR 0.0.0.0:10022:localhost:22 jumpuser@jump'</code></pre>
<h3>Web 端口转发</h3>
<p>实验室机器上跑了一个 web 服务,比如 <code>python -m SimpleHTTPServer</code>,默认我们只能在实验室机上的本地 8000 端口 (<a href="https://link.segmentfault.com/?enc=AGZ8Y%2FQlIZBTLZGs%2BZL4ig%3D%3D.DyE0NSVc44EBzerQ2%2F6Q1PwuDS2nRQO3lbQMDruKpt8%3D" rel="nofollow">http://localhost</a>:8000) 访问,假如我不在校园网环境并且希望访问该 web 服务。</p>
<pre><code class="shell">lab$ ssh -NR 0.0.0.0:18000:localhost:8000 jumpuser@jump</code></pre>
<p>这样的话我就可以在公网 ip 的 18000 端口访问该服务。</p>
<h3>代理端口转发</h3>
<p>假如实验室机器 lab 上有 HTTP 代理服务,运行在 1080 端口,希望 jump 机器可以使用该代理服务。</p>
<pre><code class="shell">lab$ ssh -NR 11080:localhost:1080 jumpuser@jump
lab$ ssh jumpuser@jump
jump$ export ALL_PROXY=http://127.0.0.1:11080
jump$ curl ip.sb</code></pre>
<h2>本地端口转发</h2>
<p>本地端口转发将远程的端口转发到本地,实现正向端口代理。刚刚我们把 lab 的 22 端口反向转发到 jump 的 10022 端口,现在再把它正向端口转发到 lab 的 20022 端口。</p>
<pre><code class="shell">lab$ ssh -NL 20022:localhost:10022 jumpuser@jump</code></pre>
<p>L 参数同样接受三个值,分别是“本地端口:目标主机:目标主机端口”,在这里本地端口是 20022,我们把 jump 机器的“本地端口” 10022 端口转发到本地端口 20022,注意这里的 localhost 是相对与 jump 机器来说的。</p>
<h2>动态端口转发</h2>
<p>假如我们可以通过跳板机来连接到内网机器 lab,我们希望访问网页时也经过该代理。或者另外一种情景,希望把未加密的数据经过 ssh 来进行加密连接。</p>
<pre><code class="shell">client$ ssh -ND 1080 ouyangsong@jump</code></pre>
<p>这样的话,client 机器的 1080 端口的数据,会经过跳板机然后到内网机器 lab 。然后在浏览器通过插件 <a href="https://link.segmentfault.com/?enc=zBnoclwff25sIXN63IN4uw%3D%3D.YWA8N6UlqAd41N3CKL%2F6GCgQWQQZpsPUhaES4nUA6ZiNaHuzyXVsNL14A7qWfUW7AFHT7lRVIeveWhCBZCvGaV5S91XLbF1p35nNC9B5BBufJ6Tiph05RKt8NJyR56SYRbKJxMVAdUl0GZikvb469w%3D%3D" rel="nofollow">Proxy SwitchyOmega</a> 设置代理为 <code>socks5://127.0.0.1:1080</code>,这样就实现了自制的内网的 VPN 。</p>
<p><a href="https://link.segmentfault.com/?enc=b7BV3LRMJcdRXR%2FSpQEZNA%3D%3D.lE4tuVpLMtP%2FWl%2FhXpiWB7iprDJDUgs3xHQwsEKhZ2%2BtIu4S8Hk5B%2FrjaGelR2qx" rel="nofollow">https://www.ouyangsong.com/po...</a></p>
Anaconda 的安装与常用命令
https://segmentfault.com/a/1190000013950872
2018-03-23T20:41:44+08:00
2018-03-23T20:41:44+08:00
ouyangsong
https://segmentfault.com/u/ouyangsong
1
<p>Anaconda 自带 Python 解释器以及数据处理常用的第三方库,可以非常方便地搭建 Python 环境。同时还自带了 Conda 用来管理第三方库,类似 Pip ,但是比 Pip 方便。建议使用 Anaconda 替换自带的 Python ,并且全部用户都可以使用 Anaconda 。</p>
<h2>安装</h2>
<p>首先去官网下载对应操作平台的<a href="https://link.segmentfault.com/?enc=p4rSUaOZY6Wyw4mDeb3xSA%3D%3D.cGRcvGHbcnEG4W3pwW4FRjnB8915yA45teAWcVGlaJ%2BHst%2FmL%2BSI4XSuVUCye3ni" rel="nofollow">安装包</a>,推荐使用国内的镜像源 Tuna <a href="https://link.segmentfault.com/?enc=TRq3aQbr5vcCYtusmtyLvg%3D%3D.mhqMH942sfv%2FW9E0CrzhKQWWom4ms4%2Fq5qsMU9m6spJ6QlC7FuWVBnx1EnDlAWL9EtZbWoxCceD2ZtpDzGG3mf4JvX6nWp0UGd3juZn1CpA%3D" rel="nofollow">下载</a>。如果觉得不喜欢 Anaconda 自带的第三方包,可以选择 <a href="https://link.segmentfault.com/?enc=MOhwgWl5A7QCKfkXonvoGQ%3D%3D.Nm60DpoNfsEqe4lDIZWsK7RaAjC3HFlRohdeExyagCDFUcIBg2uRzAcquKHVLe5H%2BylFwudolthCoszKQXsKMp0GnfDX806%2BeVdsppv8V9k%3D" rel="nofollow">Miniconda</a>。</p>
<h3>Linux</h3>
<p>为了使所有用户都使用 Anaconda 自带的 Python ,不能把 Anaconda 安装到默认的当前用户的 Home 目录。推荐安装到<code>/opt</code>目录。</p>
<pre><code class="shell">bash Miniconda3-latest-Linux-x86_64.sh -p /opt/miniconda3</code></pre>
<p>接下来需要修改全局的环境变量,以 root 用户执行如下命令。</p>
<pre><code class="shell">echo 'PATH=/opt/miniconda3/bin:$PATH' >> /etc/profile.d/miniconda.sh
source /etc/profile</code></pre>
<h3>OSX</h3>
<p>Mac安装Anaconda和Linux类似,但是还可以额外使用<a href="https://link.segmentfault.com/?enc=NVfmYmzKevC1aShQcaejTA%3D%3D.lXIoWMmpUhFxkgPhDXuqXw%3D%3D" rel="nofollow">Brew</a>命令安装。</p>
<pre><code class="shell"># 添加Homebrew-Cask源
brew tap caskroom/cask
brew cask install miniconda</code></pre>
<p>然后修改用户的环境变量,添加下面设置到<code>.zshrc</code>中。</p>
<pre><code class="shell">export PATH="/usr/local/miniconda3/bin:$PATH"
# 如果使用的是Bash,相应的修改 .bashrc
source ~/.zshrc</code></pre>
<h2>使用</h2>
<p>首先验证下Anaconda是否安装成功。</p>
<pre><code class="shell">which python
which conda</code></pre>
<p>如果输入的路径就是上一步指定的路径,那么就是安装成功。</p>
<h3>虚拟环境管理</h3>
<p>针对不同的项目,建议给每个项目创建一个虚拟环境,以防相互影响。</p>
<pre><code class="shell"># 创建虚拟环境
conda create --name mytest
# 查看所有环境
conda env list
# 激活环境
source activate mytest
# 取消环境
source deactivate mytest
# 删除环境
conda remove --name mytest --all</code></pre>
<h3>第三方库管理</h3>
<p>之前说了 Conda 是类似 Pip 的包管理命令。不过自带的包比较少,所有很多包搜索不到,这里推荐添加第三方的源 <a href="https://link.segmentfault.com/?enc=dV4Q1J%2FAYmhVgQ5KD8rkXg%3D%3D.s%2Bs5sG8o7H5iQ9n4mP%2BrR2tjR3SG5z96UjULiILmmuY%3D" rel="nofollow">Conda Forge</a>。</p>
<pre><code class="shell">conda config --add channels conda-forge
conda install <package-name>
# -n 指定环境名字,-c 指定安装源
conda install -n mytest jieba</code></pre>
<p><a href="https://link.segmentfault.com/?enc=E%2FlO%2BptH5f8ayXeJsk7HBw%3D%3D.tH2Ih%2BIULZ8xrHhYStd26zY9ANJNUgOvVjCoEKocF3w6olPuiSHAj4YLmaagJA0P" rel="nofollow">https://www.ouyangsong.com/po...</a></p>