一、文件的讀寫1.csv文件demo.csv文件內容: id,name
1,word
2,word
3,word
4,word
5,null
6,NaN
7,word
8,word
(1).讀取.csv文件
1).代碼示例import pandas as pd
# 讀取指定路徑下的文件,,并設置以第一行作為列索引
df = pd.read_csv(r'D:\Project\Python\jyputerTest\demo.csv',index_col=0)
print(df)
# 檢測數據類型是否是DataFrame
print(type(df))
# 輸出:<class 'pandas.core.frame.DataFrame'>
(2).輸出.csv文件
1).代碼示例# 將df中的指定列的數據寫到csv文件中,,并設置輸出列索引(因為此時的列索引是我們的第一列數據),設置輸出頭部
df.to_csv('grades.csv',columns=['name'],index=True,header=True)
2.Excel文件
demo.xlsx文件內容
(1).讀取Excel文件1).代碼示例# 讀取指定路徑下的Excel文件,并把第一列值作為索引讀入
df_xlsx = pd.read_excel(r'D:\Project\Python\jyputerTest\demo.xlsx',index_col=0)
(2).將DataFrame寫入Excel1).代碼示例
2).此時生成的文件的第二種讀取方式# 加載一個Excel文件對象
df_xlsx3 = pd.ExcelFile(r'D:\Project\Python\jyputerTest\demoOut.xlsx')
# 再講Excel文件對象轉成df對象,,同時指定索引列為標的第一列數據
newDf = df_xlsx3.parse('sheet1',index_col=0)
# 展示數據
newDf
3.MySQL數據庫
(1).讀寫MySQL1).代碼示例# 導入必要模塊
import pandas as pd
from sqlalchemy import create_engine
# 初始化數據庫連接,,使用pymysql模塊
# MySQL的用戶:root, 密碼:111111, 端口:3306,數據庫:test,指定編碼為utf-8(防止出現亂碼)
engine = create_engine('mysql+pymysql://root:111111@localhost:3306/test?charset=utf8')
# 查詢語句,選出demo表中的所有數據(主義)
sql = 'select * from demo;'
# read_sql_query的兩個參數: sql語句,, 數據庫連接
df = pd.read_sql_query(sql, engine)
# 輸出employee表的查詢結果
print(df)
# 新建pandas中的DataFrame, 只有id,num兩列
df2 = pd.DataFrame({'id':[1,2,3,4],'num':[12,34,56,89]})
# 將新建的DataFrame儲存為MySQL中的數據表,,不儲存index列
df2.to_sql('mydf', engine, index= False)
# 將從mysql中讀取的數據再寫到mysql數據庫中
df.to_sql('demo2', engine, index=False)
# 打印執(zhí)行成功
print('Read from and write to Mysql table successfully!')
# 在msyql的客戶端可以查詢相應的表以及表中的內容
二、DataFrame對象基本操作1.數據提取與其他操作(0).DataFrame的添加刪除操作
(1).根據索引或者列名取值# 1.整數作為索引:df.iloc[n],,默認查找第n行
df.iloc[0]
# 2.按索引提取區(qū)域行數值(區(qū)間的值是左閉右開的)
df.iloc[0:5]
# 3.列表作為索引:查找列表中數字對應行號的數據,,如,當輸入[0,2]時,,對應查找行號為0和2的數據,,而不是0-2行
df.iloc[[0,2]]
# 4.按索引提取單行的數值
df.loc[3]
# 5.提取4日之前的所有數據
df[:'2013-01-04']
# 6.設置日期為索引(date這一列的數據都是日期格式的)
df=df.set_index('date')
# 7.重設索引
'''
將排序后的索引重新排序
df.reset_index(drop)
其中drop為布爾型值:
True表示修改原始數據的索引。
False保留原始數據索引序列,。
'''
df.reset_index()
# 8.如取Name列數據的前5行
df['Name'][:5]
print(df['Name'][:5])
# 9.獲取多列數據時需要傳入一個列表對象
cols = ['name', 'province_name', 'city_name', 'city_code', 'area', 'addr']
# 10.獲取多列數據的前2行數據
df[cols][:2]
print(df[cols])
# 11.判斷city列中的所有值是否有值為'beijing'
df['city'].isin(['beijing'])
# 12.判斷city列里是否包含beijing和shanghai,然后將符合條件的數據提取出來
df.loc[df['city'].isin(['beijing','shanghai'])]
(2).查看DataFrame的前后幾行
(3).展示DataFrame列名# 1.展示列名
col_names = df.columns
print(col_names)
# 2查看下col_names格式
type(col_names)
# 3.將col_names轉化為list
col_list = col_names.tolist()
print(col_list)
(4).矢量化操作(批量操作)
2.常見操作示例小結(0).字符串(str)的處理print(df['股票代碼']) # 取當前列的所有值
print('sz000002'[:2]) # 根據索引取當前指定列的值
# 加上str之后可以使用常見的字符串函數對整列進行操作;
print(df['股票代碼'].str[:2]) # 通過索引獲取字符串中字符(不寫默認是從0開始)
print(df['股票代碼'].str.upper()) # 將字符串中的小寫字母轉為大寫字母,。
print(df['股票代碼'].str.lower()) # 轉換字符串中所有大寫字符為小寫。
print(df['股票代碼'].str.len()) # 計算字符串的長度,length
df['股票代碼'].str.strip() # strip操作,把字符串兩邊的空格去掉
print(df['股票代碼'].str.contains('sh')) # 判斷字符串中是否包含某些特定字符
print(df['股票代碼'].str.replace('szzzz', 'sz')) # 進行替換,將szzzz替換成sz;格式:str.replace(old, new[, max]),其中max是指定替換不超過max次
print(df['新浪概念'].str.split(';')) # 對字符串進行分割
print(df['新浪概念'].str.split(';').str[:2]) # 分割后取第一個位置
print(df['新浪概念'].str.split(';', expand=True)) # 分割后并且將數據分列
# 8.字符串.str的常見操作
# 定義變量: name = 'abcdaaaefg'
(1). strip() 去除前后端的空格
(2).判斷變量name是否以al開頭,,并輸出 開頭查找 startswith()
print(name.startswith('ab'))
(3).判斷變量name是否以Nb結尾,,并輸出 結尾查找 endswith()
print(name.endswith('Nb'))
(4).將name變量中所有的a替換為 p 替換方法 replace(old,new)
print(name.replace('a','p'))
(5).判斷name變量中對應的值'a'出現幾次 查找元素出現的次數方法 count()
print(name.count('a'))
(6).判斷name變量中前四位中'a'出現幾次. 解釋:先把name的前四位找出來,然后用count計算l的出現次數
a = name[0:5]
print(a.count('a'))
(7).找到name變量中'f'的索引; index方法:找不到會報錯;find方法:找不到返回-1
print(name.index('f'))
print(name.find('f'))
(8).for循環(huán)遍歷集合
s='fsfaf'
for i in s:
print(i)
# str()函數: 返回一個對象的string格式(也就是將對象轉成字符串)
str(object='', encoding='utf-8') # 可以通過object參數指定傳入的對象
str() # 也可以直接將對象傳入
(1).數據清洗1).去重清洗
2).空值清洗①.刪除空值并重新設置索引# (1),刪除空值(NaN):調用函數先刪除空值,然后重新設置索引! ★★
df = df.dropna().reset_index(drop=True)
# 2.從mysql數據庫中取值
from sqlalchemy import create_engine
import pymysql
db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
import pandas as pd
df = pd.read_sql('testavg',db)
# 直接調用df對象的dropna方法來對缺失值進行刪除
# dropna( axis = 0 /1 )參數axis表示軸選擇,,axis=0 代表行,axis=1 代表列,。
df.dropna()
②.使用默認值填充空值
③.使用平均值填充空值# 3.用平均值進行填充
from sqlalchemy import create_engine
import pymysql
db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
import pandas as pd
import numpy as np
df = pd.read_sql('testavg',db)
# df.isna() 判斷出df對象中的空值(如果為空,,則為true)
# np.where(df.isna()) 返回空值對應的行索引和列索引以series的方式封裝在了元組中
# np.where(df.isna())[0] 從元組中取出行索引(0代表行);
# 只寫一個數字的時候是取一行,,然后放在列表中就可以取多行,,并且可以加逗號之后就取某一列。
# df.iloc[np.where(df.isna())[0]] 根據索引將值取出來,返回的是一個新的df對象
# for a in df.iloc[np.where(df.isna())[0]].itertuples(): 遍歷df對象,返回的是元組,可以通過下標取出對應的值,;并對其進行遍歷(在遍歷df對象的時候就可以采用這種方法)
#
# row[0] 得到每一個行索引(這是df對象的)
# 下一行的3是指的第3列
# df.iloc[row[0],3] 通過行索引和指定的列取的每個對應的df對象的值(也就是對應的每一個空值所在的位置)
# df['jobname'] 根據標簽取值,,取到當前標簽下的所有值
# df['jobname'].str.contains(row[3]) 比較兩個字符串是否向相等用contains方法,而且contains方法是在str下的,;返回值是布爾類型的
# df[df['jobname'].str.contains(row[3])] 取出當前比較的結果,,得到的是布爾類型的值(此時就已經將row[3]中對應的每個屬性的所有值進行分組了)
# df[df['jobname'].str.contains(row[3])] 根據不同的布爾值,從而取出每組中的值
# df[df['jobname'].str.contains(row[3])].mean() 對所有的數值型數據進行求平均值,,
# df[df['jobname'].str.contains(row[3])].mean()['salary'] 只取出salary標簽的平均值
# int(df[df['jobname'].str.contains(row[3])].mean()['salary']) 強制轉換成int類型的
# df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])].mean()['salary']) 最后對空值賦值從而實現替換空值
for row in df.iloc[np.where(df.isna())[0]].itertuples():
df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])].mean()['salary'])
print(df)
④.使用眾數填充空值
⑤.使用中位數填充空值# 5.用中位數進行填充
from sqlalchemy import create_engine
import pymysql
db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
import pandas as pd
import numpy as np
df = pd.read_sql('testavg',db)
# df.isna() 判斷出df對象中的空值(如果為空,則為true)
# np.where(df.isna()) 返回空值對應的行索引和列索引以series的方式封裝在了元組中
# np.where(df.isna())[0] 從元組中取出行索引(0代表行),;
# 只寫一個數字的時候是取一行,,然后放在列表中就可以取多行,并且可以加逗號之后就取某一列,。
# df.iloc[np.where(df.isna())[0]] 根據索引將值取出來,,這里是傳入的列表,然后就可以取出多行來,。
# for a in df.iloc[np.where(df.isna())[0]].itertuples(): 將DataFrame迭代為元祖,;并對其進行遍歷。
#
# row[0] 得到每一個行索引(這是df對象的)
# df.iloc[row[0],3] 通過行索引和指定的列取的每個對應的df對象的值(也就是對應的每一個空值)
# df['jobname'] 根據標簽取值,,取到當前標簽下的所有值
# df['jobname'].str.contains(row[3]) 比較兩個字符串是否向相等用contains方法,,而且contains方法是在str下的;返回值是布爾類型的
# df[df['jobname'].str.contains(row[3])] 取出當前比較的結果,,得到的是布爾類型的值(此時就已經將row[3]中對應的每個屬性的所有值進行分組了)
# df[df['jobname'].str.contains(row[3])] 根據不同的布爾值,,從而取出每組中的值
# df[df['jobname'].str.contains(row[3])]['salary'] 取出salary標簽的值
# df[df['jobname'].str.contains(row[3])]['salary'].median() salary標簽下的中位數
# int(df[df['jobname'].str.contains(row[3])]['salary'].median()) 強制轉換成int類型的
# df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])]['salary'].median()) 最后對空值賦值從而實現替換空值
# row 是代表每一行的數據
for row in df.iloc[np.where(df.isna())[0]].itertuples():
df.iloc[row[0],3] = int(df[df['jobname'].str.contains(row[3])]['salary'].median())
⑥.使用插值法填充空值
⑦.將空值作為一個新的label處理from sqlalchemy import create_engine
import pymysql
db = create_engine('mysql+pymysql://root:111111@localhost:3306/migrate')
import pandas as pd
# 數據來源是MySQL
df = pd.read_sql('testavg',db)
# 設置一個新的lable存下空值
df['空值'] = df.iloc[np.where(df.isna())[0]].salary
⑧.使用KNN填補缺失值
3).異常值處理①判斷是否有異常值import numpy as np
# ser1表示傳入DataFrame的某一列
def three_sigma(ser):
# 求的是某一列的平均值啊(也就u)
mean_value = ser.mean()
# 求標準差(也就是σ)
std_value = ser1.std()
# 位于(u-3σ,u+3σ)區(qū)間的數據是正常的,不在這個值的數據便是異常值,其中s值的是方差
# 一旦發(fā)現異常值就標注為true,否則標注為false,所以這里的返回值是布爾值
rule = (ser < mean_value-3*std_value)|(ser > ser.mean()+3*ser1.std())
# 獲取異常數據,因為當前列就是一個Series,可以直接用布爾值取數據
outrange = ser[rule]
# 將判斷出來的異常值返回
return outrange
②處理異常值(替換)
4).數據標準化
# 實現函數:
def programmer_2():
# 定義文件的路徑
datafile = path + '/data/normalization_data.xls'
# 將利用Pandas將數據讀取進來
data = pd.read_excel(datafile, header=None)
# 最小-最大規(guī)范化
print((data - data.min()) / (data.max() - data.min()))
# 零-均值規(guī)范會(也就是標準差標準化)
print((data - data.mean()) / data.std())
# 小數定標規(guī)范化
print(data / 10**np.ceil(np.log10(data.abs().max())))
5).連續(xù)屬性的離散化
6).對小數位數的精度處理(1)對于要求較小的精度
將精度高的浮點數轉換成精度低的浮點數。
1.round()內置方法
round()不是簡單的四舍五入的處理方式,。
>>> round(2.5)
2
>>> round(1.5)
2
>>> round(2.675)
3
>>> round(2.675, 2)
2.67
round()如果只有一個數作為參數,,不指定位數的時候,返回的是一個整數,,而且是最靠近的整數(這點上類似四舍五入),。
但是當出現.5的時候,兩邊的距離都一樣,,round()取靠近的偶數
(2)如果需要精確小數位數到17位之后,,參考以下博客:
https://www.cnblogs.com/yfz1552800131/p/5363297.html
7).刪除滿足指定條件的元素
(2).篩選操作# 0.根據指定的條件,篩選出相關拿數據
print(df['股票代碼'] == 'sh000002') # 判斷股票代碼是否等于sz000002
print(df[df['股票代碼'] == 'sz000002']) # 將判斷為True的輸出:選取股票代碼等于sz000002的行
print(df[df['股票代碼'].isin(['sz000002', 'sz000003 ', 'sz000004'])]) # 選取股票代碼等于sz000002,sz000003,sz000004的行
print(df[df['收盤價'] >= 24.0]) # 選取收盤價大于24的行
print(df[(df.index >= '03/12/2016') & (df.index <= '06/12/2016')]) # &:與操作(并且)
print(df[(df.index >= '03/12/2016') | (df.index <= '06/12/2016')]) # |:或操作(或者)
# 使用與,或,非三個條件配合大于,小于,等于對數據進行篩選,并進行計數和求和,。
# 1,使用'與'進行篩選
df.loc[(df['age'] > 25) & (df['city'] == 'beijing'), ['id','city','age','category','gender']]
# 2,使用'或'進行篩選
df.loc[(df['age'] > 25) | (df['city'] == 'beijing'), ['id','city','age','category','gender']].sort(['age'])
# 3,使用'非'條件進行篩選
df.loc[(df['city'] != 'beijing'), ['id','city','age','category','gender']].sort(['id'])
# 4,對篩選后的數據按city列進行計數
df.loc[(df['city'] != 'beijing'), ['id','city','age','category','gender']].sort(['id']).city.count()
# 5,使用query函數進行篩選
df.query('city == ['beijing', 'shanghai']')
# 6,對篩選后的結果按prince進行求和
df.query('city == ['beijing', 'shanghai']').price.sum()
(3).常見的數據統計與計算函數
# 數據采樣,計算標準差,協方差和相關系數
# 簡單的數據隨機采樣(也就是說從df中隨機取出三組數)
df.sample(n=3)
# 手動設置采樣權重
weights = [0, 0, 0, 0, 0.5, 0.5]
df.sample(n=2, weights=weights)
# 采樣后不放回
df.sample(n=6, replace=False)
# 采樣后放回
df.sample(n=6, replace=True)
# 數據表描述性統計
df.describe().round(2).T # round函數設置顯示小數位,T表示轉置
# 計算列的標準差
df['price'].std()
# 計算兩個字段間的協方差
df['price'].cov(df['m-point'])
# 數據表中所有字段間的協方差
df.cov()
# 兩個字段的相關性分析 ★★
df['price'].corr(df['m-point']) #相關系數在-1到1之間,接近1為正相關,接近-1為負相關,0為不相關
# 數據表的相關性分析
df.corr()
(4).排序操作
# coding=utf-8
import pandas as pd
import numpy as np
# 以下實現排序功能,。
# 先創(chuàng)建一個Series對象和DataFrame對象
s=pd.Series([3,4,1,6],index=['b','a','d','c'])
df = pd.DataFrame([[2,4,1,5],[3,1,4,5],[5,1,4,2]],columns=['b','a','d','c'],index=['one','two','three'])
df = pd.DataFrame([['2012-3-4','2013-2-4','2014-11-4','2016-2-24'],
['2019-1-24','2018-12-4','2017-11-14','2017-2-24'],
['2012-3-4','2013-2-4','2014-11-4','2016-2-24']],columns=['a','b','c','d'])
print(df)
print(s)
# 降序排序,默認是升序排序★★
list.sort(reverse=True)
# 'series通過索引進行排序:'
print(s.sort_index())
# 'series通過值進行排序:'
print(s.sort_values())
# 'dataframe根據行索引進行降序排序(排序時默認升序,調節(jié)ascending參數):'
print(df.sort_index(ascending=False))
# 'dataframe根據列索引進行排序:'
print(df.sort_index(axis=1))
# 'dataframe根據指定的列的值進行排序:'
print(df.sort_values(by='a'))
# '通過多個列索引進行排序:'
print(df.sort_values(by=['a','c']))
# by參數指定按照那一列進行排序,acsending參數指定是升序還是降序,默認是升序即acsending=1,當acsending=0時降序
print(df.sort_values(by=['交易日期'], ascending=1))
# 按照多列進行排序
print(df.sort_values(by=['股票名稱', '交易日期'], ascending=[1, 1]))
# 在指定排序字段的時候可以直接使用字段名進行排序,inplace參數表示在排序的之后修改原來df中的值,,False則是只返回排序的結果
df.sort_values('xxx',inplace=True)
(5).數據匯總
# 測試2:
# 創(chuàng)建一個測試對象
df = pd.DataFrame({ 'A': ['a', 'b', 'a', 'c', 'a', 'c', 'b', 'c'],
'B': [2, 8, 1, 4, 3, 2, 5, 9],
'C': [102, 98, 107, 104, 115, 87, 92, 123]})
Out[1]:
A B C
0 a 2 102
1 b 8 98
2 a 1 107
3 c 4 104
4 a 3 115
5 c 2 87
6 b 5 92
7 c 9 123
'''
# 按A列分組(groupby),獲取其他列的均值(mean)
df.groupby('A').mean()
'''
Out[2]:
B C
A
a 2.0 108.000000
b 6.5 95.000000
c 5.0 104.666667
'''
# 按先按A列再按B列進行分組,
df.groupby(['A','B']).mean()
'''
Out[3]:
C
A B
a 1 107
2 102
3 115
b 5 92
8 98
c 2 87
4 104
9 123
'''
# 分組后,可以選取單列數據,或者多個列組成的列表(list)進行運算
s = df.groupby('A')
s['B'].mean() # 只選擇B列
s[['B','C']].mean() # 選擇B列和C列,用一個列表封裝起來
'''
Out[4]:
A
a 2.0
b 6.5
c 5.0
Out[5]:
B C
A
a 2.0 108.000000
b 6.5 95.000000
c 5.0 104.666667
'''
# 可以針對不同的列選用不同的聚合方法
s.agg({'B':'mean', 'C':'sum'})
'''
Out[6]:
B C
A
a 2.0 324
b 6.5 190
c 5.0 314
'''
(6).pandas中的'size,shape,len,count,value_counts'之間的不同使用
(7).pandas對日期的處理小結
1).設置當前列為索引列# 當前df對象中含有一個date列,,其中都是時間格式的數據
# (1).先對數據進行類型轉換,方便接下來的篩選數據和統計數據
df['date'] = pd.to_datetime(df['date']) #將數據類型轉換為日期類型
df = df.set_index('date') # 將date設置為index
2).按日期篩選數據①.按年度獲取數據
②.獲取某月的數據print(df['2013-11'])
③.獲取具體某天的數據
3).按日期統計數據①.按年統計數據# 1.按年統計,,但仍以完整的日期顯示
print(df.resample('AS').sum())
# 'AS'是每年第一天為開始日期, 'A是每年最后一天
'''
number
date
2013-01-01 51
2014-01-01 453
2015-01-01 743
2016-01-01 1552
2017-01-01 92
'''
# 2.按年統計數據并按年顯示
print(df.resample('AS').sum().to_period('A'))
'''
number
date
2013 51
2014 453
2015 743
2016 1552
2017 92
'''
②.按季度統計數據
③.按月統計數據# 1.按月統計數據,,但仍以完整的日期顯示
print(df.resample('M').sum().head())
'''
number
date
2013-10-31 10
2013-11-30 14
2013-12-31 27
2014-01-31 16
2014-02-28 4
'''
# 'MS'是每個月第一天為開始日期, 'M'是每個月最后一天
'''
number
date
2013-10-31 10
2013-11-30 14
2013-12-31 27
2014-01-31 16
2014-02-28 4
'''
# 2.按月統計并按月顯示
print(df.resample('M').sum().to_period('M').head())
'''
number
date
2013-10 10
2013-11 14
2013-12 27
2014-01 16
2014-02 4
'''
④.按周統計數據
4).時間差的數據轉換# 這樣計算時間差會出現單位:days
res = pd.DataFrame(pd.to_datetime(data['LOAD_TIME']) - pd.to_datetime(data['FFP_DATE'])) # 234days
# 只取時間,而不帶days
res_new = res.dt.days # 234
# 這樣可以將日期的數據轉換成月份格式的數據()
res.map(lambda x: x / np.timedelta64(30 * 24 * 60, 'm'))
(8).Numpy的where的用法
(9).數據表的合并操作# (1).merge
'''
多表之間的連接也是非常常見的數據庫操作,連接分內連接和外連接,在數據庫語言中通過join關鍵字實現.
注意:默認情況下,merge函數實現的是兩個表之間的內連接,即返回兩張表中共同部分的數據,。
可以通過how參數設置連接的方式,left為左連接;right為右連接;outer為外連接。
可以使用on關鍵字實現指定按某個列去合并!
'''
# 這里的df和df1是兩個Dataframe對象
df=pd.merge(df,df1,how='inner',on='name') # 匹配合并:交集(這里兩個df中都有name列)
df_left=pd.merge(df,df1,how='left') # 左連接(此時會保留左表(df)的數據)
df_right=pd.merge(df,df1,how='right') # 右連接(此時會保留右表(df1)的數據)
df_outer=pd.merge(df,df1,how='outer') # 并集(兩張表取并集)
# (2).append
result = df1.append(df2)
'''
df1:
A B
0 A0 B0
1 A1 B1
df2:
A B
3 A3 B3
4 A4 B4
result:
A B
0 A0 B0
1 A1 B1
3 A3 B3
4 A4 B4
'''
# (3).join
result = left.join(right, on='key') # 根據key列,將左表和右表聯系在一起
'''
left:
A B
0 A0 B0
1 A1 B1
right:
C D
0 C3 D3
1 C4 D4
result:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
'''
# (4).concat
frames = [df1, df2, df3] # 指定三個df對象
result = pd.concat(frames) # 將三個df對象的值合并到一個df對象中
'''
df1:
A B
0 A0 B0
1 A1 B1
df2:
A B
3 A3 B3
4 A4 B4
df3:
A B
5 A5 B5
6 A6 B6
result:
A B
0 A0 B0
1 A1 B1
3 A3 B3
4 A4 B4
5 A5 B5
6 A6 B6
'''
|
|
來自: NeighborMrSun > 《語法技巧》