整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          每天學點JavaScript編程-ES6(ECMAScript5)@“對象”擴展運算符

          天學點JavaScript編程--ES6(ECMAScript5)之“對象”擴展運算符

          對象的擴展運算符

          《數組的擴展》中,已經介紹過擴展運算符(...)。ES2018 將這個運算符引入了對象。

          解構賦值

          對象的解構賦值用于從一個對象取值,相當于將目標對象自身的所有可遍歷的(enumerable)、但尚未被讀取的屬性,分配到指定的對象上面。所有的鍵和它們的值,都會拷貝到新對象上面

          let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
          x // 1
          y // 2
          z // { a: 3, b: 4 }

          上面代碼中,變量z是解構賦值所在的對象。它獲取等號右邊的所有尚未讀取的鍵(a和b),將它們連同值一起拷貝過來。

          由于解構賦值要求等號右邊是一個對象,所以如果等號右邊是undefined或null,就會報錯,因為它們無法轉為對象。

          let { ...z } = null; // 運行時錯誤
          let { ...z } = undefined; // 運行時錯誤

          解構賦值必須是最后一個參數,否則會報錯。

          let { ...x, y, z } = someObject; // 句法錯誤
          let { x, ...y, ...z } = someObject; // 句法錯誤

          上面代碼中,解構賦值不是最后一個參數,所以會報錯。

          注意,解構賦值的拷貝是淺拷貝,即如果一個鍵的值是復合類型的值(數組、對象、函數)、那么解構賦值拷貝的是這個值的引用,而不是這個值的副本。

          let obj = { a: { b: 1 } };
          let { ...x } = obj;
          obj.a.b = 2;
          x.a.b // 2

          上面代碼中,x是解構賦值所在的對象,拷貝了對象obj的a屬性。a屬性引用了一個對象,修改這個對象的值,會影響到解構賦值對它的引用。

          另外,擴展運算符的解構賦值,不能復制繼承自原型對象的屬性

          let o1 = { a: 1 };
          let o2 = { b: 2 };
          o2.__proto__ = o1;
          let { ...o3 } = o2;
          o3 // { b: 2 }
          o3.a // undefined

          上面代碼中,對象o3復制了o2,但是只復制了o2自身的屬性,沒有復制它的原型對象o1的屬性。

          下面是另一個例子。

          const o = Object.create({ x: 1, y: 2 });
          o.z = 3;
          ?
          let { x, ...newObj } = o;
          let { y, z } = newObj;
          x // 1
          y // undefined
          z // 3

          上面代碼中,變量x是單純的解構賦值,所以可以讀取對象o繼承的屬性;變量y和z是擴展運算符的解構賦值,只能讀取對象o自身的屬性,所以變量z可以賦值成功,變量y取不到值。ES6 規定,變量聲明語句之中,如果使用解構賦值,擴展運算符后面必須是一個變量名,而不能是一個解構賦值表達式,所以上面代碼引入了中間變量newObj,如果寫成下面這樣會報錯。

          let { x, ...{ y, z } } = o;
          // SyntaxError: ... must be followed by an identifier in declaration contexts

          解構賦值的一個用處,是擴展某個函數的參數,引入其他操作。

          function baseFunction({ a, b }) {
            // ...
          }
          function wrapperFunction({ x, y, ...restConfig }) {
            // 使用 x 和 y 參數進行操作
            // 其余參數傳給原始函數
            return baseFunction(restConfig);
          }

          上面代碼中,原始函數baseFunction接受a和b作為參數,函數wrapperFunction在baseFunction的基礎上進行了擴展,能夠接受多余的參數,并且保留原始函數的行為

          擴展運算符

          對象的擴展運算符(...)用于取出參數對象的所有可遍歷屬性,拷貝到當前對象之中。

          let z = { a: 3, b: 4 };
          let n = { ...z };
          n // { a: 3, b: 4 }

          由于數組是特殊的對象,所以對象的擴展運算符也可以用于數組。

          let foo = { ...['a', 'b', 'c'] };
          foo
          // {0: "a", 1: "b", 2: "c"}

          如果擴展運算符后面是一個空對象,則沒有任何效果

          {...{}, a: 1}
          // { a: 1 }

          如果擴展運算符后面不是對象,則會自動將其轉為對象。

          // 等同于 {...Object(1)}
          {...1} // {}

          上面代碼中,擴展運算符后面是整數1,會自動轉為數值的包裝對象Number{1}。由于該對象沒有自身屬性,所以返回一個空對象。

          下面的例子都是類似的道理。

          // 等同于 {...Object(true)}
          {...true} // {}
          ?
          // 等同于 {...Object(undefined)}
          {...undefined} // {}
          ?
          // 等同于 {...Object(null)}
          {...null} // {}

          但是,如果擴展運算符后面是字符串,它會自動轉成一個類似數組的對象,因此返回的不是空對象。

          {...'hello'}
          // {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}

          對象的擴展運算符等同于使用Object.assign()方法。

          let aClone = { ...a };
          // 等同于
          let aClone = Object.assign({}, a);

          上面的例子只是拷貝了對象實例的屬性,如果想完整克隆一個對象,還拷貝對象原型的屬性,可以采用下面的寫法。

          // 寫法一
          const clone1 = {
            __proto__: Object.getPrototypeOf(obj),
            ...obj
          };
          ?
          // 寫法二
          const clone2 = Object.assign(
            Object.create(Object.getPrototypeOf(obj)),
            obj
          );
          ?
          // 寫法三
          const clone3 = Object.create(
            Object.getPrototypeOf(obj),
            Object.getOwnPropertyDescriptors(obj)
          )

          上面代碼中,寫法一的**proto**屬性在非瀏覽器的環境不一定部署,因此推薦使用寫法二和寫法三。

          擴展運算符可以用于合并兩個對象。

          let ab = { ...a, ...b };
          // 等同于
          let ab = Object.assign({}, a, b);

          如果用戶自定義的屬性,放在擴展運算符后面,則擴展運算符內部的同名屬性會被覆蓋掉。

          let aWithOverrides = { ...a, x: 1, y: 2 };
          // 等同于
          let aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
          // 等同于
          let x = 1, y = 2, aWithOverrides = { ...a, x, y };
          // 等同于
          let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 });

          上面代碼中,a對象的x屬性和y屬性,拷貝到新對象后會被覆蓋掉。

          這用來修改現有對象部分的屬性就很方便了。

          let newVersion = {
            ...previousVersion,
            name: 'New Name' // Override the name property
          };

          上面代碼中,newVersion對象自定義了name屬性,其他屬性全部復制自previousVersion對象。


          如果把自定義屬性放在擴展運算符前面,就變成了設置新對象的默認屬性值。

          let aWithDefaults = { x: 1, y: 2, ...a };
          // 等同于
          let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
          // 等同于
          let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);

          與數組的擴展運算符一樣,對象的擴展運算符后面可以跟表達式

          const obj = {
            ...(x > 1 ? {a: 1} : {}),
            b: 2,
          };

          擴展運算符的參數對象之中,如果有取值函數get,這個函數是會執行的。

          // 并不會拋出錯誤,因為 x 屬性只是被定義,但沒執行
          let aWithXGetter = {
            ...a,
            get x() {
              throw new Error('not throw yet');
            }
          };
          
          // 會拋出錯誤,因為 x 屬性被執行了
          let runtimeError = {
            ...a,
            ...{
              get x() {
                throw new Error('throw now');
              }
            }
          };

          看完兩件小事

          把這篇文章分享給你的朋友 / 交流群,讓更多的人看到,一起進步,一起成長!

          自Medium

          作者:Rudolf H?hn機器之心編譯

          參與:李詩萌、張倩

          在本文中,作者從 Pandas 的簡介開始,一步一步講解了 Pandas 的發展現狀、內存優化等問題。這是一篇最佳實踐教程,既適合用過 Pandas 的讀者,也適合沒用過但想要上手的小白。

          通過本文,你將有望發現一到多種用 pandas 編碼的新方法。

          本文包括以下內容:

          • Pandas 發展現狀;
          • 內存優化;
          • 索引;
          • 方法鏈;
          • 隨機提示。

          在閱讀本文時,我建議你閱讀每個你不了解的函數的文檔字符串(docstrings)。簡單的 Google 搜索和幾秒鐘 Pandas 文檔的閱讀,都會使你的閱讀體驗更加愉快。

          Pandas 的定義和現狀

          什么是 Pandas?

          Pandas 是一個「開源的、有 BSD 開源協議的庫,它為 Python 編程語言提供了高性能、易于使用的數據架構以及數據分析工具」。總之,它提供了被稱為 DataFrame 和 Series(對那些使用 Panel 的人來說,它們已經被棄用了)的數據抽象,通過管理索引來快速訪問數據、執行分析和轉換運算,甚至可以繪圖(用 matplotlib 后端)。

          Pandas 的當前最新版本是 v0.25.0 (https://github.com/pandas-dev/pandas/releases/tag/v0.25.0)

          Pandas 正在逐步升級到 1.0 版,而為了達到這一目的,它改變了很多人們習以為常的細節。Pandas 的核心開發者之一 Marc Garcia 發表了一段非常有趣的演講——「走向 Pandas 1.0」。

          演講鏈接:https://www.youtube.com/watch?v=hK6o_TDXXN8

          用一句話來總結,Pandas v1.0 主要改善了穩定性(如時間序列)并刪除了未使用的代碼庫(如 SparseDataFrame)。

          數據

          讓我們開始吧!選擇「1985 到 2016 年間每個國家的自殺率」作為玩具數據集。這個數據集足夠簡單,但也足以讓你上手 Pandas。

          數據集鏈接:https://www.kaggle.com/russellyates88/suicide-rates-overview-1985-to-2016

          在深入研究代碼之前,如果你想重現結果,要先執行下面的代碼準備數據,確保列名和類型是正確的。


          import pandas as pdimport numpy as npimport os# to download https://www.kaggle.com/russellyates88/suicide-rates-overview-1985-to-2016
          data_path = 'path/to/folder/'df = (pd.read_csv(filepath_or_buffer=os.path.join(data_path, 'master.csv')) .rename(columns={'suicides/100k pop' : 'suicides_per_100k', ' gdp_for_year ($) ' : 'gdp_year', 'gdp_per_capita ($)' : 'gdp_capita', 'country-year' : 'country_year'}) .assign(gdp_year=lambda _df: _df['gdp_year'].str.replace(',','').astype(np.int64)) )
          

          提示:如果你讀取了一個大文件,在 read_csv(https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html)中參數設定為 chunksize=N,這會返回一個可以輸出 DataFrame 對象的迭代器。

          這里有一些關于這個數據集的描述:

          >>> df.columnsIndex(['country', 'year', 'sex', 'age', 'suicides_no', 'population', 'suicides_per_100k', 'country_year', 'HDI for year', 'gdp_year', 'gdp_capita', 'generation'], dtype='object')
          

          這里有 101 個國家、年份從 1985 到 2016、兩種性別、六個年代以及六個年齡組。有一些獲得這些信息的方法:

          可以用 unique() 和 nunique() 獲取列內唯一的值(或唯一值的數量);

          >>> df['generation'].unique()array(['Generation X', 'Silent', 'G.I. Generation', 'Boomers', 'Millenials', 'Generation Z'], dtype=object)>>> df['country'].nunique()101
          

          可以用 describe() 輸出每一列不同的統計數據(例如最小值、最大值、平均值、總數等),如果指定 include='all',會針對每一列目標輸出唯一元素的數量和出現最多元素的數量;

          可以用 head() 和 tail() 來可視化數據框的一小部分。

          通過這些方法,你可以迅速了解正在分析的表格文件。

          內存優化

          在處理數據之前,了解數據并為數據框的每一列選擇合適的類型是很重要的一步。

          在內部,Pandas 將數據框存儲為不同類型的 numpy 數組(比如一個 float64 矩陣,一個 int32 矩陣)。

          有兩種可以大幅降低內存消耗的方法。

          import pandas as pd
          def mem_usage(df: pd.DataFrame) -> str: """This method styles the memory usage of a DataFrame to be readable as MB. Parameters ---------- df: pd.DataFrame Data frame to measure. Returns ------- str Complete memory usage as a string formatted for MB. """ return f'{df.memory_usage(deep=True).sum() / 1024 ** 2 : 3.2f} MB'
          def convert_df(df: pd.DataFrame, deep_copy: bool = True) -> pd.DataFrame: """Automatically converts columns that are worth stored as ``categorical`` dtype. Parameters ---------- df: pd.DataFrame Data frame to convert. deep_copy: bool Whether or not to perform a deep copy of the original data frame. Returns ------- pd.DataFrame Optimized copy of the input data frame. """ return df.copy(deep=deep_copy).astype({ col: 'category' for col in df.columns if df[col].nunique() / df[col].shape[0] < 0.5})
          

          Pandas 提出了一種叫做 memory_usage() 的方法,這種方法可以分析數據框的內存消耗。在代碼中,指定 deep=True 來確保考慮到了實際的系統使用情況。

          memory_usage():https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.memory_usage.html

          了解列的類型(https://pandas.pydata.org/pandas-docs/stable/getting_started/basics.html#basics-dtypes)很重要。它可以通過兩種簡單的方法節省高達 90% 的內存使用:

          • 了解數據框使用的類型;
          • 了解數據框可以使用哪種類型來減少內存的使用(例如,price 這一列值在 0 到 59 之間,只帶有一位小數,使用 float64 類型可能會產生不必要的內存開銷)

          除了降低數值類型的大小(用 int32 而不是 int64)外,Pandas 還提出了分類類型:https://pandas.pydata.org/pandas-docs/stable/user_guide/categorical.html

          如果你是用 R 語言的開發人員,你可能覺得它和 factor 類型是一樣的。

          這種分類類型允許用索引替換重復值,還可以把實際值存在其他位置。教科書中的例子是國家。和多次存儲相同的字符串「瑞士」或「波蘭」比起來,為什么不簡單地用 0 和 1 替換它們,并存儲在字典中呢?

          categorical_dict = {0: 'Switzerland', 1: 'Poland'}
          

          Pandas 做了幾乎相同的工作,同時添加了所有的方法,可以實際使用這種類型,并且仍然能夠顯示國家的名稱。

          回到 convert_df() 方法,如果這一列中的唯一值小于 50%,它會自動將列類型轉換成 category。這個數是任意的,但是因為數據框中類型的轉換意味著在 numpy 數組間移動數據,因此我們得到的必須比失去的多。

          接下來看看數據中會發生什么。

          >>> mem_usage(df)10.28 MB>>> mem_usage(df.set_index(['country', 'year', 'sex', 'age']))5.00 MB>>> mem_usage(convert_df(df))1.40 MB>>> mem_usage(convert_df(df.set_index(['country', 'year', 'sex', 'age'])))1.40 MB
          

          通過使用「智能」轉換器,數據框使用的內存幾乎減少了 10 倍(準確地說是 7.34 倍)。

          索引

          Pandas 是強大的,但也需要付出一些代價。當你加載 DataFrame 時,它會創建索引并將數據存儲在 numpy 數組中。這是什么意思?一旦加載了數據框,只要正確管理索引,就可以快速地訪問數據。

          訪問數據的方法主要有兩種,分別是通過索引和查詢訪問。根據具體情況,你只能選擇其中一種。但在大多數情況中,索引(和多索引)都是最好的選擇。我們來看下面的例子:

          >>> %%time>>> df.query('country == "Albania" and year == 1987 and sex == "male" and age == "25-34 years"')CPU times: user 7.27 ms, sys: 751 μs, total: 8.02 ms# ==================>>> %%time>>> mi_df.loc['Albania', 1987, 'male', '25-34 years']CPU times: user 459 μs, sys: 1 μs, total: 460 μs
          

          什么?加速 20 倍?

          你要問自己了,創建這個多索引要多長時間?

          %%timemi_df = df.set_index(['country', 'year', 'sex', 'age'])CPU times: user 10.8 ms, sys: 2.2 ms, total: 13 ms
          

          通過查詢訪問數據的時間是 1.5 倍。如果你只想檢索一次數據(這種情況很少發生),查詢是正確的方法。否則,你一定要堅持用索引,CPU 會為此感激你的。

          .set_index(drop=False) 允許不刪除用作新索引的列。

          .loc[]/.iloc[] 方法可以很好地讀取數據框,但無法修改數據框。如果需要手動構建(比如使用循環),那就要考慮其他的數據結構了(比如字典、列表等),在準備好所有數據后,創建 DataFrame。否則,對于 DataFrame 中的每一個新行,Pandas 都會更新索引,這可不是簡單的哈希映射。

          >>> (pd.DataFrame({'a':range(2), 'b': range(2)}, index=['a', 'a']) .loc['a']) a ba 0 0a 1 1
          

          因此,未排序的索引可以降低性能。為了檢查索引是否已經排序并對它排序,主要有兩種方法:

          %%time>>> mi_df.sort_index()CPU times: user 34.8 ms, sys: 1.63 ms, total: 36.5 ms>>> mi_df.index.is_monotonicTrue
          

          更多詳情請參閱:

          • Pandas 高級索引用戶指南:https://pandas.pydata.org/pandas-docs/stable/user_guide/advanced.html;
          • Pandas 庫中的索引代碼:https://github.com/pandas-dev/pandas/blob/master/pandas/core/indexing.py。

          方法鏈

          使用 DataFrame 的方法鏈是鏈接多個返回 DataFrame 方法的行為,因此它們都是來自 DataFrame 類的方法。在現在的 Pandas 版本中,使用方法鏈是為了不存儲中間變量并避免出現如下情況:

          import numpy as npimport pandas as pddf = pd.DataFrame({'a_column': [1, -999, -999], 'powerless_column': [2, 3, 4], 'int_column': [1, 1, -1]}) df['a_column'] = df['a_column'].replace(-999, np.nan) df['power_column'] = df['powerless_column'] ** 2 df['real_column'] = df['int_column'].astype(np.float64) df = df.apply(lambda _df: _df.replace(4, np.nan)) df = df.dropna(how='all')
          

          用下面的鏈替換:

          df = (pd.DataFrame({'a_column': [1, -999, -999], 'powerless_column': [2, 3, 4], 'int_column': [1, 1, -1]}) .assign(a_column=lambda _df: _df['a_column'].replace(-999, np.nan)) .assign(power_column=lambda _df: _df['powerless_column'] ** 2) .assign(real_column=lambda _df: _df['int_column'].astype(np.float64)) .apply(lambda _df: _df.replace(4, np.nan)) .dropna(how='all') )
          

          說實話,第二段代碼更漂亮也更簡潔。

          方法鏈的工具箱是由不同的方法(比如 apply、assign、loc、query、pipe、groupby 以及 agg)組成的,這些方法的輸出都是 DataFrame 對象或 Series 對象(或 DataFrameGroupBy)。

          了解它們最好的方法就是實際使用。舉個簡單的例子:

          (df .groupby('age') .agg({'generation':'unique'}) .rename(columns={'generation':'unique_generation'})# Recommended from v0.25# .agg(unique_generation=('generation', 'unique')))
          

          獲得每個年齡范圍中所有唯一年代標簽的簡單鏈

          在得到的數據框中,「年齡」列是索引。

          除了了解到「X 代」覆蓋了三個年齡組外,分解這條鏈。第一步是對年齡組分組。這一方法返回了一個 DataFrameGroupBy 對象,在這個對象中,通過選擇組的唯一年代標簽聚合了每一組。

          在這種情況下,聚合方法是「unique」方法,但它也可以接受任何(匿名)函數。

          在 0.25 版本中,Pandas 引入了使用 agg 的新方法:https://dev.pandas.io/whatsnew/v0.25.0.html#groupby-aggregation-with-relabeling。

          (df .groupby(['country', 'year']) .agg({'suicides_per_100k': 'sum'}) .rename(columns={'suicides_per_100k':'suicides_sum'})# Recommended from v0.25# .agg(suicides_sum=('suicides_per_100k', 'sum')) .sort_values('suicides_sum', ascending=False) .head(10))
          

          用排序值(sort_values)和 head 得到自殺率排前十的國家和年份

          (df .groupby(['country', 'year']) .agg({'suicides_per_100k': 'sum'}) .rename(columns={'suicides_per_100k':'suicides_sum'})# Recommended from v0.25# .agg(suicides_sum=('suicides_per_100k', 'sum')) .nlargest(10, columns='suicides_sum'))
          

          用排序值 nlargest 得到自殺率排前十的國家和年份

          在這些例子中,輸出都是一樣的:有兩個指標(國家和年份)的 MultiIndex 的 DataFrame,還有包含排序后的 10 個最大值的新列 suicides_sum。

          「國家」和「年份」列是索引。

          nlargest(10) 比 sort_values(ascending=False).head(10) 更有效。

          另一個有趣的方法是 unstack:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.unstack.html,這種方法允許轉動索引水平。


          (mi_df .loc[('Switzerland', 2000)] .unstack('sex') [['suicides_no', 'population']])
          

          「age」是索引,列「suicides_no」和「population」都有第二個水平列「sex」。

          下一個方法 pipe 是最通用的方法之一。這種方法允許管道運算(就像在 shell 腳本中)執行比鏈更多的運算。

          管道的一個簡單但強大的用法是記錄不同的信息。

          def log_head(df, head_count=10): print(df.head(head_count)) return df
          def log_columns(df): print(df.columns) return df
          def log_shape(df): print(f'shape = {df.shape}') return df
          

          和 pipe 一起使用的不同記錄函數。

          舉個例子,我們想驗證和 year 列相比,country_year 是否正確:

          (df .assign(valid_cy=lambda _serie: _serie.apply( lambda _row: re.split(r'(?=\d{4})', _row['country_year'])[1] == str(_row['year']), axis=1)) .query('valid_cy == False') .pipe(log_shape))
          

          用來驗證「country_year」列中年份的管道。

          管道的輸出是 DataFrame,但它也可以在標準輸出(console/REPL)中打印。

          shape = (0, 13)
          

          你也可以在一條鏈中用不同的 pipe。

          (df .pipe(log_shape) .query('sex == "female"') .groupby(['year', 'country']) .agg({'suicides_per_100k':'sum'}) .pipe(log_shape) .rename(columns={'suicides_per_100k':'sum_suicides_per_100k_female'})# Recommended from v0.25# .agg(sum_suicides_per_100k_female=('suicides_per_100k', 'sum')) .nlargest(n=10, columns=['sum_suicides_per_100k_female']))
          

          女性自殺數量最高的國家和年份。

          生成的 DataFrame 如下所示:

          索引是「年份」和「國家」。

          標準輸出的打印如下所示:

          shape = (27820, 12)shape = (2321, 1)
          

          除了記錄到控制臺外,pipe 還可以直接在數據框的列上應用函數。

          from sklearn.preprocessing import MinMaxScaler
          def norm_df(df, columns): return df.assign(**{col: MinMaxScaler().fit_transform(df[[col]].values.astype(float)) for col in columns}) 
          for sex in ['male', 'female']: print(sex) print( df .query(f'sex == "{sex}"') .groupby(['country']) .agg({'suicides_per_100k': 'sum', 'gdp_year': 'mean'}) .rename(columns={'suicides_per_100k':'suicides_per_100k_sum', 'gdp_year': 'gdp_year_mean'}) # Recommended in v0.25 # .agg(suicides_per_100k=('suicides_per_100k_sum', 'sum'), # gdp_year=('gdp_year_mean', 'mean')) .pipe(norm_df, columns=['suicides_per_100k_sum', 'gdp_year_mean']) .corr(method='spearman') ) print('\n')
          

          自殺數量是否和 GDP 的下降相關?是否和性別相關?

          上面的代碼在控制臺中的打印如下所示:

          male suicides_per_100k_sum gdp_year_meansuicides_per_100k_sum 1.000000 0.421218gdp_year_mean 0.421218 1.000000
          
          female suicides_per_100k_sum gdp_year_meansuicides_per_100k_sum 1.000000 0.452343gdp_year_mean 0.452343 1.000000
          

          深入研究代碼。norm_df() 將一個 DataFrame 和用 MinMaxScaling 擴展列的列表當做輸入。使用字典理解,創建一個字典 {column_name: method, …},然后將其解壓為 assign() 函數的參數 (colunmn_name=method, …)。

          在這種特殊情況下,min-max 縮放不會改變對應的輸出:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.corr.html,它僅用于參數。

          在(遙遠的?)未來,緩式評估(lazy evaluation)可能出現在方法鏈中,所以在鏈上做一些投資可能是一個好想法。

          最后(隨機)的技巧

          下面的提示很有用,但不適用于前面的任何部分:

          itertuples() 可以更高效地遍歷數據框的行;

          >>> %%time>>> for row in df.iterrows(): continueCPU times: user 1.97 s, sys: 17.3 ms, total: 1.99 s>>> for tup in df.itertuples(): continueCPU times: user 55.9 ms, sys: 2.85 ms, total: 58.8 ms
          

          注意:tup 是一個 namedtuple

          join() 用了 merge();在 Jupyter 筆記本中,在代碼塊的開頭寫上 %%time,可以有效地測量時間;UInt8 類:https://pandas.pydata.org/pandas-docs/stable/user_guide/gotchas.html#support-for-integer-na支持帶有整數的 NaN 值;

          記住,任何密集的 I/O(例如展開大型 CSV 存儲)用低級方法都會執行得更好(盡可能多地用 Python 的核心函數)。

          還有一些本文沒有涉及到的有用的方法和數據結構,這些方法和數據結構都很值得花時間去理解:

          數據透視表:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.pivot.html?source=post_page---------------------------

          時間序列/日期功能:https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html?source=post_page---------------------------;

          繪圖:https://pandas.pydata.org/pandas-docs/stable/user_guide/visualization.html?source=post_page---------------------------。

          總結

          希望你可以因為這篇簡短的文章,更好地理解 Pandas 背后的工作原理,以及 Pandas 庫的發展現狀。本文還展示了不同的用于優化數據框內存以及快速分析數據的工具。希望對現在的你來說,索引和查找的概念能更加清晰。最后,你還可以試著用方法鏈寫更長的鏈。

          這里還有一些筆記:https://github.com/unit8co/medium-pandas-wan?source=post_page---------------------------

          除了文中的所有代碼外,還包括簡單數據索引數據框(df)和多索引數據框(mi_df)性能的定時指標。

          熟能生巧,所以繼續修煉技能,并幫助我們建立一個更好的世界吧。

          PS:有時候純用 Numpy 會更快。

          原文鏈接:https://medium.com/unit8-machine-learning-publication/from-pandas-wan-to-pandas-master-4860cf0ce442

          odash, Moment, Axios, Async... 這些是有用的JavaScript庫,您希望在許多Vue.js應用程序中使用這些庫。

          但是,隨著項目的發展,您將將代碼分離為單個文件組件和模塊文件。您還可能希望在不同的環境中運行您的應用程序,以允許服務器呈現。

          除非您找到一種簡單而健壯的方法將這些JavaScript庫包含在組件和模塊文件中,否則它們將是一個麻煩!



          多么不在Vue.js項目中包含一個庫

          全局變量

          將庫添加到項目中的天真方法是通過將庫附加到window目的:

          entry.js

          JavaScript

          window._ = require('lodash');

          MyComponent.vue

          JavaScript

          export default { created() { console.log(_.isEmpty() ? 'Lodash everywhere!' : 'Uh oh..'); }}

          針對窗口變量的情況很長,但是,特別是在本討論中,它們不適用于服務器呈現。當應用程序在服務器上運行時,window對象將是未定義的,因此試圖訪問屬性將以錯誤結束。

          導入每個文件

          另一種二流方法是將庫導入到每個文件中:

          MyComponent.vue

          JavaScript

          import _ from 'lodash';
          export default {
          created() {
          console.log(_.isEmpty() ? 'Lodash is available here!' : 'Uh oh..');
          }
          }

          這是可行的,但它并不是很干燥,而且基本上是一種痛苦:您必須記住將它導入到每個文件中,如果您停止在該文件中使用它,則再次刪除它。如果您沒有正確地設置您的構建工具,那么您的構建中可能會有多個相同庫的副本。

          更好的方法

          在Vue項目中使用JavaScript庫的最干凈和最健壯的方法是將其代理到Vue Prototype對象的屬性。讓我們這樣做,將時間、日期和時間庫添加到我們的項目中:

          entry.js

          JavaScript

          import moment from 'moment';

          Object.definePrototype(Vue.prototype, '$moment', { value: moment });

          由于所有組件都從Vue Prototype對象繼承它們的方法,這將使所有組件都可以在沒有全局變量或手動導入的任何組件中自動使用。可以簡單地在任何實例/組件中訪問this.$moment:

          MyNewComponent.vue

          JavaScript

          export default {
          created() {
          console.log('The time is ' . this.$moment().format("HH:mm"));
          }
          }

          現在讓我們花時間了解一下這是如何工作的。

          Object.defineProperty

          我們通常會設置如下對象屬性:

          JavaScript

          Vue.prototype.$moment = moment;

          你可以在這里做,但是Object.defineProperty相反,我們可以使用descriptor。描述符允許我們設置一些低級別的詳細信息,例如我們的屬性是否可寫,以及它是否在枚舉期間顯示在for循環等等。


          在日常的JavaScript中,我們通常不會考慮這個問題,因為99%的時間我們都不需要這樣的細節來分配屬性。但是在這里,它給了我們一個明顯的優勢:用描述符創建的屬性是只讀默認情況下。

          這意味著,一些缺少咖啡的開發人員(可能是您)無法在組件中做一些愚蠢的事情,從而破壞一切:

          JavaScript

          this.$http = 'Assign some random thing to the instance method';

          this.$http.get('/'); // TypeError: this.$http.get is not a function


          相反,我們的只讀實例方法保護我們的庫,如果您試圖覆蓋它,您將得到“TypeError:NotAssistto只讀屬性”。

          $

          您會注意到,我們將我們的庫代理到一個以美元符號“$”為前綴的屬性名。您可能還看到了其他屬性和方法,如$refs, $on, $mount,等等,它們也有這個前綴。

          雖然不是必需的,但前綴被添加到屬性中,以提醒缺乏咖啡的開發人員(再次),這是一個歡迎您使用的公共API屬性或方法,與實例的其他屬性不同,這些屬性可能只是用于Vue的內部使用。

          作為一種基于原型的語言,JavaScript中沒有(真正的)類,因此它沒有“私有”和“公共”變量或“靜態”方法。這個公約是一個溫和的替代,我認為是值得遵循的。


          您還會注意到要使用您使用的庫this.libraryName這可能并不令人驚訝,因為它現在是一個實例方法。

          但是,這樣做的一個結果是,與全局變量不同,您必須確保在使用庫時處于正確的范圍內。在回調方法中,無法訪問this你的圖書館住的地方。

          FAT箭頭回調是確保您保持在正確范圍內的一個很好的解決方案:

          JavaScript

          this.$http.get('/').then(res => {
          if (res.status !== 200) {
          this.$http.get('/') // etc
          // Only works in a fat arrow callback.
          }
          });


          為什么不把它變成插件呢?

          如果您計劃在多個Vue項目中使用一個庫,或者您想要與世界共享它,您可以將其構建到您自己的插件中!

          插件抽象了復雜性,并允許您在項目中簡單地執行以下操作以添加所選庫:

          JavaScript

          import MyLibraryPlugin from 'my-library-plugin';

          Vue.use(MyLibraryPlugin);

          使用這兩行,我們可以在任何組件中使用庫,就像我們可以使用Vue路由器、Vuex和其他利用Vue.use.


          編寫插件

          首先,為插件創建一個文件。在本例中,我將制作一個插件,將Axios添加到所有Vue實例和組件中,因此我將調用該文件axios.js.

          主要要了解的是,插件必須公開install方法,它將Vue構造函數作為第一個參數:

          axios.js

          JavaScript

          export default {
          install: function(Vue) {
          // Do stuff
          }
          }

          現在,我們可以使用前面的方法將庫添加到Prototype對象中:

          axios.js

          JavaScript

          import axios from 'axios';
          export default {
          install: function(Vue,) {
          Object.defineProperty(Vue.prototype, '$http', { value: axios });
          }
          }


          這個use實例方法是將庫添加到項目中所需的全部內容。例如,我們現在可以像這樣輕松地添加Axios庫:

          entry.js

          JavaScript

          import AxiosPlugin from './axios.js';
          Vue.use(AxiosPlugin);
          new Vue({
          created() {
          console.log(this.$http ? 'Axios works!' : 'Uh oh..');
          }
          })

          獎勵:插件可選參數

          插件安裝方法可以采用可選參數。有些開發人員可能不喜歡調用他們的Axios實例方法。$http由于Vue Resource通常被命名為Vue Resource,所以讓我們使用一個可選的參數來允許他們將其更改為他們喜歡的任何東西:

          axios.js

          JavaScript

          import axios from 'axios';
          export default {
          install: function(Vue, name = '$http') {
          Object.defineProperty(Vue.prototype, name, { value: axios });
          }
          }


          entry.js

          JavaScript

          import AxiosPlugin from './axios.js';
          Vue.use(AxiosPlugin, '$axios');
          new Vue({
          created() {
          console.log(this.$axios ? 'Axios works!' : 'Uh oh..');
          }
          })

          為感謝您對我們的認可,特意準備了一些IT入門和進階的干貨

          包括:Java、UI設計、H5前端、Python+人工智能、軟件測試和新媒體運營六大學科視頻資料。以及IT就業大禮包。

          線上視頻、音頻,隨時學習觀看

          關注我們并私信“資料”即可獲取。


          主站蜘蛛池模板: 老湿机一区午夜精品免费福利| 成人精品一区二区不卡视频| 日本一区二区在线免费观看| 一区免费在线观看| 中文字幕日韩人妻不卡一区| 久久精品一区二区东京热| 国产香蕉一区二区在线网站| 日本精品视频一区二区| 香蕉一区二区三区观| 国产高清一区二区三区| 亚洲日韩精品无码一区二区三区| 在线播放偷拍一区精品| 性无码一区二区三区在线观看| 亚洲日韩一区精品射精| 插我一区二区在线观看| 国产suv精品一区二区6| 国产成人精品第一区二区| 亚洲一区二区三区偷拍女厕| 日韩一区二区三区在线精品| 日本无卡码免费一区二区三区| 国产精品伦一区二区三级视频| 国产精品一区二区av不卡| 秋霞电影网一区二区三区| 国产亚洲综合精品一区二区三区| 亚洲精品无码一区二区| 中文字幕视频一区| 日韩在线一区二区| 午夜视频在线观看一区| 精品国产一区二区三区在线 | 国产精品亚洲一区二区三区在线观看 | 91在线看片一区国产| 亚洲电影一区二区| 乱精品一区字幕二区| 亚洲视频一区在线| 亚洲色无码专区一区| 风间由美在线亚洲一区| 视频一区二区在线观看| 久久99久久无码毛片一区二区| 无码日韩精品一区二区人妻 | 国精品无码一区二区三区在线蜜臀| 久久se精品一区精品二区国产|