「R」用purrr實現迭代

2021-02-13 優雅R

本文來源於 2018 年學習《R for Data Science》寫的筆記。一起複習一下吧~

函數有3個好處:

除了函數,減少重複代碼的另一種工具是迭代,它的作用在於可以對多個輸入執行同一種處理,比如對多個列或多個數據集進行同樣的操作。

迭代方式主要有兩種:

準備工作

purrr是tidyverse的核心r包之一,提供了一些更加強大的編程工具。(讀者可以點擊原文獲取小抄

library(tidyverse)#> ─ Attaching packages ─────────────────────────────────────────────────── tidyverse 1.2.1 ─#> ✔ ggplot2 3.0.0     ✔ purrr   0.2.5#> ✔ tibble  1.4.2     ✔ dplyr   0.7.6#> ✔ tidyr   0.8.1     ✔ stringr 1.3.1#> ✔ readr   1.1.1     ✔ forcats 0.3.0#> ─ Conflicts ──────────────────────────────────────────────────── tidyverse_conflicts() ─#> ✖ dplyr::filter() masks stats::filter()#> ✖ dplyr::lag()    masks stats::lag()

for循環與函數式編程

因為R是一門函數式程式語言,我們可以先將for循環包裝在函數中,然後再調用函數,而不是使用for循環,因此for循環在R中不像在其他程式語言中那麼重要。

為了說明函數式編程,我們先利用下面簡單的數據框進行一些思考:

df = tibble(    a = rnorm(10),    b = rnorm(10),    c = rnorm(10),    d = rnorm(10))

如果想要計算每列的均值,我們使用for循環完成任務:

output = vector("double", length(df))
for (i in seq_along(df)) { output[[i]] = mean(df[[i]])}
output#> [1] 0.45635 -0.17938 0.32879 0.00263

然後我們可能意識到需要頻繁地計算每列的均值,因此將代碼提取出來,轉換為一個函數:

col_mean = function(df) {    output = vector("double", length(df))    for ( i in seq_along(df)) {        output[i] = mean(df[[i]])    }        output}

然後我們覺得可能還需要這樣計算每列的中位數和標準差,因此複製粘貼了col_mean(),並使用相應的median()和sd()函數替換了mean()函數:

col_median = function(df) {    output = vector("double", length(df))    for ( i in seq_along(df)) {        output[i] = median(df[[i]])    }        output}
col_sd = function(df) { output = vector("double", length(df)) for ( i in seq_along(df)) { output[i] = sd(df[[i]]) } output}

(有時候我還真這麼幹的。)

哎呀,我們又複製粘貼了2次代碼,因此是不是該思考下如何擴展一個代碼讓它同時發揮幾個函數的功能呢?這段代碼的大部分是一個for循環,而且如果不仔細很難看出3個函數有什麼差別。

通過添加支持函數到每列的參數,我們可以使用同一個函數解決3個問題:

col_summary = function(df, fun){    out = vector("double", length(df))    for (i in seq_along(df)) {        out[i] = fun(df[[i]])    }    out}
col_summary(df, median)#> [1] 0.4666 0.0269 0.6161 0.0573col_summary(df, mean)#> [1] 0.45635 -0.17938 0.32879 0.00263

將函數作為參數傳入另一個函數的做法是一種非常強大的功能,我們需要花些時間理解這種思想,但絕對是值得的。接下來我們將學習和使用purrr包,它提供的函數可以替代很多常見的for循環應用。R基礎包中的apply應用函數族也可以完成類似的任務,但purrr包的函數更一致,也更容易學習。

使用purrr函數替代for循環的目的是將常見的列表問題分解為獨立的幾部分

對於列表的單個元素,我們能找到解決辦法嗎?如果可以,我們就能使用purrr將該方法擴展到列表的所有元素。如果我們面臨的是一個複雜的問題,那麼將其分解為可行的子問題,然後依次解決。使用purrr,我們可以解決子問題,然後用管道將其組合起來。映射函數

先對向量進行循環,然後對其每一個元素進行一番處理,最後保存結果。這種模式太普遍了,因而purrr包提供了一個函數族替我們完成這種操作。每種類型的輸出都有一個相應的函數:

每個函數都使用一個向量(注意列表可以作為遞歸向量看待)作為輸入,並對向量的每個元素應用一個函數,然後返回和輸入向量同樣長度的一個新向量。向量的類型由映射函數的後綴決定。

使用map()函數族的優勢不是速度,而是簡潔:它可以讓我們的代碼更易編寫,也更易閱讀。

下面是進行上一節一樣的操作:

library(purrr)
map_dbl(df, mean)#> a b c d #> 0.45635 -0.17938 0.32879 0.00263map_dbl(df, median)#> a b c d #> 0.4666 0.0269 0.6161 0.0573map_dbl(df, sd)#> a b c d #> 0.608 1.086 0.797 0.873

**與for循環相比,映射函數的重點在於需要執行的操作(即mean()、median()和sd()),而不是在所有元素中循環所需的跟蹤記錄以及保存結果。使用管道時這一點尤為突出:

df %>% map_dbl(mean)#>        a        b        c        d #>  0.45635 -0.17938  0.32879  0.00263df %>% map_dbl(median)#>      a      b      c      d #> 0.4666 0.0269 0.6161 0.0573df %>% map_dbl(sd)#>     a     b     c     d #> 0.608 1.086 0.797 0.873

map_*()和col_summary()具有以下幾點區別:

所有的purrr函數都是用C實現的,這讓它們的速度非常快,但犧牲了一些可讀性。第二個參數可以是一個公式、一個字符向量或一個整型向量。map_*()使用...向.f傳遞一些附加參數,供每次調用時使用快捷方式

對於第二個參數.f,我們可以使用幾種快捷方式來減少輸入量。比如我們現在想對某個數據集中的每一個分組都擬合一個線性模型,下面示例將mtcars數據集拆分為3個部分(按照氣缸值分類),並對每個部分擬合一個線性模型:

models = mtcars %>%     split(.$cyl) %>%     map(function(df) lm(mpg ~ wt, data = df))

因為在R中創建匿名函數的語法比較複雜,所以purrr提供了一種更方便的快捷方式——單側公式:

models = mtcars %>%     split(.$cyl) %>%     map(~lm(mpg ~ wt, data = .))

上面.作為一個代詞:它表示當前列表元素(與for循環中用i表示當前索引是一樣的)。

當檢查多個模型時,有時候我們需要提取像R方這樣的摘要統計量,要想完成這個任務,我們需要先運行summary()函數,然後提取結果中的r.squared:

models %>%     map(summary) %>%     map_dbl(~.$r.squared)#>     4     6     8 #> 0.509 0.465 0.423

因為提取命名成分操作非常普遍,所以purrr提供了一種更簡單的快捷方式:使用字符串。

models %>%     map(summary) %>%     map_dbl("r.squared")#>     4     6     8 #> 0.509 0.465 0.423

對操作失敗的處理

當使用映射函數重複多次操作時,某次操作失敗的概率大大增加。這個時候我們會收到一條錯誤信息,但得不到任何結果。這讓人很惱火!我們怎麼保證不會出現一條魚腥了一鍋湯?

safely()是一個修飾函數(副詞),它接收一個函數(動詞),對其進行修改並返回修改後的函數。這樣,修改後的函數就不會拋出錯誤,相反,它總是返回由下面兩個元素組成的列表:

result - 原始結果。如果出現錯誤,那麼它就是NULLerror - 錯誤對象。如果操作成功,那麼它就是NULL

下面用log()函數進行說明:

safe_log = safely(log)str(safe_log(10))#> List of 2#>  $ result: num 2.3#>  $ error : NULL
str(safe_log("a"))#> List of 2#> $ result: NULL#> $ error :List of 2#> ..$ message: chr "數學函數中用了非數值參數"#> ..$ call : language log(x = x, base = base)#> ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

safely()函數也可以與map()共同使用:

x = list(1, 10, "a")y = x %>% map(safely(log))str(y)#> List of 3#>  $ :List of 2#>   ..$ result: num 0#>   ..$ error : NULL#>  $ :List of 2#>   ..$ result: num 2.3#>   ..$ error : NULL#>  $ :List of 2#>   ..$ result: NULL#>   ..$ error :List of 2#>   .. ..$ message: chr "數學函數中用了非數值參數"#>   .. ..$ call   : language log(x = x, base = base)#>   .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

如果將以上結果轉換為2個列表,一個列表包含所有錯誤對象,另一個列表包含所有原始結果,那麼處理起來就會更容易。我們可以使用purrr::transpose()函數輕鬆完成該任務

y = y %>% transpose()str(y)#> List of 2#>  $ result:List of 3#>   ..$ : num 0#>   ..$ : num 2.3#>   ..$ : NULL#>  $ error :List of 3#>   ..$ : NULL#>   ..$ : NULL#>   ..$ :List of 2#>   .. ..$ message: chr "數學函數中用了非數值參數"#>   .. ..$ call   : language log(x = x, base = base)#>   .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

我們可以自行決定如何處理錯誤對象,一般來說,我們應該檢查一下y中錯誤對象所對應的x值,或者使用y中的正常結果進行一些處理:

is_ok = y$error %>% map_lgl(is_null)x[!is_ok]#> [[1]]#> [1] "a"
y$result[is_ok] %>% flatten_dbl()#> [1] 0.0 2.3

purrr還提供了兩個有用的修飾函數:

與safely()類似,possibly()函數總是會成功返回。它比safely()還要簡單一些,因為可以設定出現錯誤時返回一個默認值:
x = list(1, 10, "a")x %>% map_dbl(possibly(log, NA_real_))#> [1] 0.0 2.3  NA

quietly()函數與safely()的作用基本相同,但前者結果不包含錯誤對象,而是包含輸出、消息和警告:
x = list(1, -1)x %>% map(quietly(log)) %>% str()#> List of 2#>  $ :List of 4#>   ..$ result  : num 0#>   ..$ output  : chr ""#>   ..$ warnings: chr(0) #>   ..$ messages: chr(0) #>  $ :List of 4#>   ..$ result  : num NaN#>   ..$ output  : chr ""#>   ..$ warnings: chr "產生了NaNs"#>   ..$ messages: chr(0)
x %>% map(safely(log)) %>% str()#> Warning in .f(...): 產生了NaNs#> List of 2#> $ :List of 2#> ..$ result: num 0#> ..$ error : NULL#> $ :List of 2#> ..$ result: num NaN#> ..$ error : NULL

多參數映射

前面我們提到的映射函數都是對單個輸入進行映射,但有時候我們需要多個相關輸入同步迭代,這就是map2()和pmap()函數的用武之地

例如我們想模擬幾個均值不同的隨機正態分布,我們可以使用map完成這個任務:

mu = list(5, 10, -3)mu %>%     map(rnorm, n = 5) %>%     str()#> List of 3#>  $ : num [1:5] 5.65 6.48 6.35 4.61 4.74#>  $ : num [1:5] 8.93 8.93 10.67 10.98 8.72#>  $ : num [1:5] -4.04 -3.25 -2.16 -3.02 -2.53

如果我們想讓標準差也不同,一種方法是使用均值向量和標準差向量的索引進行迭代:

sigma = list(1, 5, 10)seq_along(mu) %>%     map(~rnorm(5, mu[[.]], sigma[[.]])) %>%     str()#> List of 3#>  $ : num [1:5] 4.5 4.73 4.43 6.19 5.47#>  $ : num [1:5] 8.71 8.59 18.26 7.93 4.93#>  $ : num [1:5] -21.46 -7.94 -21.41 5.66 2.38

但這種方式比較難理解,我們使用map2()進行同步迭代:

map2(mu, sigma, rnorm, n = 5) %>% str()#> List of 3#>  $ : num [1:5] 6.08 6.72 7.59 5.21 3.99#>  $ : num [1:5] 13.44 6.81 3.61 22.29 14.29#>  $ : num [1:5] 4.05 -1.77 -2.77 0.69 -23.91

注意這裡每次調用時值發生變換的參數要放在映射函數前面,值不變的參數要放在映射函數後面。

和map()函數一樣,map2()函數也是對for循環的包裝:

map2 = function(x, y, f, ...){    out = vector("list", length(x))    for (i in seq_along(x)) {        out[[i]] = f(x[[i]], y[[i]], ...)    }    out}

(實際的map2()並不是這樣的,此處是給出R實現的一種思想)

根據這個函數,我們可以涉及map3()、map4()等等,但這樣實在無聊。purrr提供了pmap()函數,它可以將列表作為參數。如果我們想要生成均值、標準差和樣本數都不同的正態分布,可以使用:

n = list(1, 3, 5)args1 = list(n, mu, sigma)
args1 %>% pmap(rnorm) %>% str()#> List of 3#> $ : num 3.55#> $ : num [1:3] 8.4 10.9 -3.3#> $ : num [1:5] 3.9 -11.61 2.06 7.14 -16.25

如果沒有為列表元素命名,那麼pmap()在調用函數時會按照位置匹配。這樣做容易出錯而且可讀性差,因此最後使用命名參數:

args2 = list(mean = mu, sd = sigma, n = n)args2 %>%     pmap(rnorm) %>%     str()#> List of 3#>  $ : num 6.18#>  $ : num [1:3] 11.2 18 14.8#>  $ : num [1:5] -5.27 6.57 1.88 6.53 -8.35

這樣更加安全。

因為長度都相同,所以將各個參數保存在一個數據框中:

params = tibble::tribble(    ~mean, ~sd, ~n,    5, 1, 1,    10, 5, 3,    -3, 10, 5)
params %>% pmap(rnorm)#> [[1]]#> [1] 5.41#> #> [[2]]#> [1] 5.4 10.2 14.4#> #> [[3]]#> [1] -8.653 -4.457 9.747 -4.916 -0.436

調用不同的函數

還有一種更複雜的情況:不但傳給函數的參數不同,甚至函數本身也是不同的。

f = c("runif", "rnorm", "rpois")param = list(    list(min = -1, max = 1),    list(sd = 5),    list(lambda = 10))

為了處理這種情況,我們使用invoke_map()函數:

invoke_map(f, param, n = 5) %>% str()#> List of 3#>  $ : num [1:5] 0.167 -0.235 -0.366 -0.933 0.304#>  $ : num [1:5] 6.961 3.642 13.405 0.536 -2.078#>  $ : int [1:5] 8 8 8 6 11

第1個參數是一個函數列表或包含函數名稱的字符串向量。第2個參數是列表的一個列表,給出了要傳給各個函數的不同參數。隨後的參數要傳給每個函數

我們使用tribble()讓參數配對更容易:

sim = tibble::tribble(    ~f, ~params,    "runif", list(min = -1, max = 1),    "rnorm", list(sd = 5),    "rpois", list(lambda = 10))

sim %>% dplyr::mutate(sim = invoke_map(f, params, n = 10))#> # A tibble: 3 x 3#> f params sim #> <chr> <list> <list> #> 1 runif <list [2]> <dbl [10]>#> 2 rnorm <list [1]> <dbl [10]>#> 3 rpois <list [1]> <int [10]>

遊走函數

當使用函數的目的是向屏幕提供輸出或將文件保存到磁碟——重要的是操作過程而不是返回值,我們應該使用遊走函數,而不是映射函數。

下面是一個示例:

x = list(1, "a", 3)
x %>% walk(print)#> [1] 1#> [1] "a"#> [1] 3

一般來說,walk()函數不如walk2()和pwalk()實用。例如有一個圖形列表和一個文件名向量,那麼我們就可以使用pwalk()將每個文件保存到相應的磁碟位置:

library(ggplot2)
plots = mtcars %>% split(.$cyl) %>% map(~ggplot(., aes(mpg, wt)) + geom_point())paths = stringr::str_c(names(plots), ".pdf")
pwalk(list(paths, plots), ggsave, path = tempdir())#> Saving 7 x 5 in image#> Saving 7 x 5 in image#> Saving 7 x 5 in image

我們來查看一下是不是建立好了:

dir(tempdir())#> [1] "4.pdf" "6.pdf" "8.pdf"

for循環的其他模式

purrr還提供了其他一些函數,雖然這些函數的使用率低,但了解還是有必要的。本節就是對它們進行簡單介紹

預測函數

一些函數可以與返回TRUE或FALSE的預測函數一同使用。

keep()和discard()函數可以分別保留輸入中預測值為TRUE和FALSE的元素(在數據框中就是指列):

iris %>%     keep(is.factor) %>%     str()#> 'data.frame':    150 obs. of  1 variable:#>  $ Species: Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

iris %>% discard(is.factor) %>% str()#> 'data.frame': 150 obs. of 4 variables:#> $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...#> $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...#> $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...#> $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...

some()和every()函數分別用來確定預測值是否對某個元素為真以及是否對所有元素為真:

x = list(1:5, letters, list(10))

x %>% some(is_character)#> [1] TRUE
x %>% every(is_vector)#> [1] TRUE

detect()可以找出預測值為真的第一個元素,detect_index()可以返回該元素的索引。

x = sample(10)x#>  [1] 10  8  5  7  4  1  2  9  3  6
x %>% detect(~ . >5)#> [1] 10
x %>% detect_index(~ . >5)#> [1] 1

head_while()和tail_while()分別從向量的開頭和結尾找出預測值為真的元素:

x %>%     head_while(~ . > 5)#> [1] 10  8
x %>% tail_while(~ . > 5)#> [1] 6

歸約和累計

操作一個複雜的列表,有時候我們想要不斷合併兩個預算兩個元素(基礎函數Reduce幹的事情)。

dfs = list(    age = tibble(name = "John", age = 30),    sex = tibble(name = c("John", "Mary"), sex = c("M", "F")),    trt = tibble(name = "Mary", treatment = "A"))
dfs %>% reduce(full_join)#> Joining, by = "name"#> Joining, by = "name"#> # A tibble: 2 x 4#> name age sex treatment#> <chr> <dbl> <chr> <chr> #> 1 John 30 M <NA> #> 2 Mary NA F A

這裡我們使用reduce結合dplyr中的full_join()將它們輕鬆合併為一個數據框。

reduce()函數使用一個「二元函數」(即兩個基本輸入),將其不斷應用於一個列表,直到最後只剩下一個元素。

累計函數與歸約函數類似,但會保留中間結果,比如下面求取累計和:

x = sample(10)
x#> [1] 9 10 8 5 6 2 3 4 7 1x %>% accumulate(`+`)#> [1] 9 19 27 32 38 40 43 47 54 55

相關焦點

  • 「背黑鍋」的英文是什麼?用英文聊聊職場黑暗面!
    [同事自己登入了 LinkedIn 然後現在他的技能欄裡多了一條「用嘴呼吸」。][I told all my colleagues at work that I have a twin so that when I see them in public I don't have to talk to them.]
  • 用錯了!答辯結束頁別再寫「謝謝聆聽」了!
    其實,「聆聽」這個詞,用、錯、了!所以,「聆聽」是中國傳統文化裡的敬辭,演講者自己用「感謝聆聽」,就意味著把自己放在長者、尊者的位置。如果你答辯的時候,導師正好知道這個詞的含義.而最常見、最萬能的內容就是「謝謝」&「Thanks」,誰都不得罪~
  • 什麼是「自動化思維」?
    他就會發現,自己一直以來的假設「辜負了大家的信任」,根本就是不存在的。03 用控制思維替代自動化思維當你明確辨識出自己的「自動化思維」之後,對於其中一些無效的思維 —— 亦即於事無補。反而會影響工作和生活的思維,可以進行主動幹預,嘗試用正向的「控制思維」去取代。
  • 辦公室實用句型「加班」、「請假」、「代班」、「出差」英文如何表達?
    打工、實習難免需要「請假」、找人「代班」對上班族來說「加班」更是常態偶爾還需要「出差」想去海外實習或到外商公司上班的朋友們
  • 拜託別再用「 Support 」啦!
    \ 任何淺嘗輒止的訓練都是扯淡 /「 Endorse 」. ‍最開始聽到這個詞是在美國大選,演講中常有這個詞的出現,現在在各個領域都非常流行,也非常好用。想像一個工作場景,你希望老闆在接下來的會議上支持你的觀點,你會說May I have your support? 沒有上下文的時候,老闆是不是可能會理解成,你需要額外的人力、經費來解決這個問題?「 Endorse 」就能完美的避免這種誤解。
  • 辨析 「省略」≠「割愛」你造嗎?
    「割愛(かつあい)」。 「省略」和「割愛」意思相近,都有「捨去」的意思,但在商務日語中很少有人能正確區分使用。大多數人會把「割愛」當成「省略」的意思來用,其實兩者還是有所區別的。 然而,「割愛」原本是佛教用語,表示「割捨難以放棄的東西。」與「省略」相比,「割愛」更加強調的是放棄、捨去時那種「不忍」的內心情感。當公務員要去其他地方任職或者大學職員要調到其他大學工作都可以用「割愛」來表示,如「割愛採用」「割愛退職」「割愛人事」。
  • 求你們不要用colleague表示「同事」了
    大家都喜歡用colleague這個詞,因為字典裡查到的「同事」就是colleague。其實colleague是比較英式的說法,而美式英語裡常用coworker。美式英語裡也有colleague,但是一個很正式的詞,常用在政府或者其他專業領域,普通公司則通用coworker,更接地氣。就像「同僚」比「同事」更接地氣一樣。
  • 這2個騷操作,輕鬆實現Excel跟Word文檔「資料同步」!
    有一天Excel做了個表,Word說:哎呀兄弟不如借我用用唄。Excel說沒問題呀兄弟,但是你要記得弄成數據能同步的那種喲,不然我更新了數據,但你那邊還是初版,很尷尬的哦。那該怎麼做呢?今天,院長為大家帶來兩個小方法,實現Word和Excel聯動。Ⅰ.
  • 都是「簡歷」,CV 和 resume 區別是什麼?
    所以,有人建議,CV 不應當翻譯成「簡歷」,最好翻譯成「履歷」。CV 通常用於申請一些學術類項目或者工作崗位。RESUME讀作:英式 /ˈrezjʊmeɪ/;美式 /ˌrezʊˈmeɪ/resume 作為「簡歷」的含義,來自法語單詞 résumé(法語含義為「總結」),所以還是借鑑了法語的發音規則。注意:不要和英語動詞 resume 的發音混淆起來。
  • 辦公室生存現狀之英文大盤點「加班」、「請假」、「代班」、「出差」地道英文怎麼說?
    畢業季,很多小夥伴開始忙於實習,要拍個畢業照,難免需要「請假」、找人「代班」對上班族來說「加班」更是常態偶爾還需要「出差」這些都是辦公室生存常態~而這些常態如何地道地用英文表達呢?當你請假的時候,需要找人幫忙暫代工作,可以用 fill in for sb 這個短語。 fill in 本身的意思是「填空」, for 後面加上代理的對象。
  • 用「鑽石型」談話結構打開心扉
    化學老師說:鑽石就是個碳。物理老師說:自然界最硬的就是鑽石。歷史老師說:33億年前,火山帶著巖漿,巖漿帶著鑽石,一起來到了人間,語文老師說那是愛的結晶,是你的淚水和愛,因你而璀璨!」最後他總結,戴比爾斯說:「鑽石恆久遠,一顆永流傳!」現在,我可以告訴你一種心理諮詢師關於鑽石的想法,那就是「鑽石型談話結構」,它可以讓你打開對話的空間,拉近彼此內心的距離。
  • 「另一方面」你還在用 on the other hand 呀?
    (這才是真正的學習過程)注意:用 on the other hand 當然也是正確的,只不過我們在4/6級、雅思託福等的寫作考試中,我相信在一百個考生當中會有九十九個在形容「另一方面」的時候就只會用 on the other hand ,對於考官們來說是一件很厭煩的事情,我們多學幾個表達方法,不光可以豐富我們的表達方法,更可以讓考官們刮目相看呢
  • 回復客戶「感謝你的關心」,「關心」的英語怎麼說?
    如果想向客戶表達「感謝你的關心」,怎麼說呢?我看有人寫 Thanks for your care,因為「關心」嘛,第一反應就是care,但你不用在這裡,因為care單獨拿出來是行動上「關心照顧」的意思,即take care of,客戶又沒有照顧你,說thanks for your care會讓人一頭霧水。那要怎麼說呢?
  • 「了解しました」你用對了嗎?
    在客戶的電話或郵件裡,接受到對方的指示和要求後,很多人會說「了解しました」或者「了解いたしました」。但是你知道嗎?「了解」是不能亂用的。這種看似尊敬的表現,在商務禮儀中究竟出了什麼問題呢?△ 了解しました「~しました」雖然是尊敬的表現,但是對待上司,或者比較重要的客戶時還是儘量不要使用。△ 了解いたしました同樣,作為尊敬的意思是沒錯的,但用「承知」更好。
  • 迭代法
    迭代法也稱輾轉法,是一種不斷用變量的舊值遞推新值的過程,跟迭代法相對應的是直接法(或者稱為一次解法),即一次性解決問題。
  • 「上班族」的英文是?用英文聊坐辦公室的優缺點
    一起來用英文聊聊當上班族的好處吧!Bob 在和已經在上班的學長 Mike 聊天,他們在聊什麼呢?別急別急,先來學學以下單詞!office worker / desk jockey (n.) 上班族 office 意指「辦公室」,office worker 指的即是「在辦公室上班的人;上班族」。
  • 「星·人物」何敏樺——「了不起的」女孩
    「我覺得我是一個沒有什麼章法的管理者,我可能會教同事一些「野路子」。因為我們做社區營造類型的項目,很多時候不能太「循規蹈矩」。如果我們要開拓一些新的服務,我會更願意拋一個主題出來,集思廣益,同事想到什麼就說出來一起討論,怎麼好玩怎麼來,我不會說「你這個想法不行」、「你這樣做不可以」,我們很多服務都是用群策群力的方式構思出來的,比如五邊地的改造,出來以後效果也很不錯。」
  • 「CC」、「RSVP」是什麼?商務英文書信常用縮略詞一次搞清楚!
    商務往來時,常會用 email 當作溝通工具,但是常用的英文簡寫你都認識嗎?下次與外國客戶溝通時,記得用上這些商業英文書信縮寫!1. CC 副本CC 是 carbon copy 的縮略詞,其中 carbon 是「碳」的意思。
  • 【經驗分享】我欣賞的產品是「克制」與「收斂」的
    我用的日曆產品就是 Web 的 Google 日曆,他包含日曆以及 task。在 iOS 上使用默認的日曆工具,同步 Google 日曆。Google 日曆裡通常都是會議記錄,但是經常你會發現,當會議很多的時候,記錄不記錄是沒什麼意義的,你還是會挑選最重要的來參加。當會議很少的時候,不記錄你也會記得。而 Google 日曆裡的 task 基本都是我的「靈光一現」的散點。
  • 「芬克斯餐廳」的原則
    有一位名叫羅斯恰爾斯的猶太人,在耶路撒冷開了一家名為「芬克斯」的小酒吧餐廳,餐廳的面積不大,只有三十平方公尺,但它卻聲名遠揚。有一天,他接到一個訂位電話,那人用十分委婉的口氣和他商量說:「我有十個隨從,他們將和我一起前往你的餐廳,為了方便,你能謝絕其他顧客嗎?」羅斯恰爾斯毫不猶豫的說:「我歡迎你們來,但要謝絕其他顧客,這不可能。」打電話的不是別人,是美國前國務卿季辛吉,他是在訪問中東的公務行程即將結束前,在別人的推薦下,打算光顧「芬克斯餐廳」的。