.

Сделать репост в соц сети!

Показаны сообщения с ярлыком Rstudio. Показать все сообщения
Показаны сообщения с ярлыком Rstudio. Показать все сообщения

вторник, 21 апреля 2020 г.

Построение нормированной гистограммы в R с использованием dplyr и pipeline



Такая задача часто возникает в анализе данных. Давайте посмотрим ее решение на примере Toy example

Создаем данные
 df = data.frame(a = sample(c('a', 'b', 'c', 'd'), 1000, replace = TRUE), 
                b = sample(c('x', 'y', 'zizuzi'), 1000, replace = TRUE),
c = rnorm(1000)) 
Загружаем необходимые библиотеки
library(dplyr) # преобразование данных
library(ggplot2) # визуализация 
library(scales) # на диаграмме значения преобразовывает в %
Далее собственно преобразование

воскресенье, 19 апреля 2020 г.

Наши HR-аналитики в Гонг- Конге

Интервью с Дарьей Ворожейкиной - People Analytics Consultant at Schneider Electric
(Hong Kong)

Дарья, день добрый!

Вы сейчас работаете ....

Я работаю консультантом по people аналитике (HR аналитик) в Гонконге в международной компании Fortune 500 в области управления энергией.

воскресенье, 22 марта 2020 г.

R. Книга рецептов


Один из редких случаев, когда книгу по аналитике можно посоветовать как начинающим, так и профессионалам.

R. Книга рецептов



Выполняйте анализ данных с помощью R быстро и эффективно, используя свыше
R. Книга рецептов
275 практических рецептов, представленных в этом расширенном издании. Язык R предоставляет все, что вам нужно для работы со статистикой, но его структуру может быть сложно освоить. Эти ориентированные на задачи рецепты помогут вам сразу же приступить к работе. Решения варьируются от базовых задач до ввода и вывода, общей статистики, графики и линейной регрессии.
Каждый рецепт посвящен определенной задаче и включает в себя обсуждение, которое объясняет решение и дает представление о том, как оно работает. Если вы новичок, эта книга поможет вам сориентироваться. Если вы промежуточный пользователь, она расшевелит вашу память и расширит ваши горизонты. Ваша работа будет сделана быстрее, и вы узнаете больше о R в процессе прочтения.

Оглавление

  • Глава 1. Начало работы и получение справочной информации
  • Глава 2. Немного основ
  • Глава 3. Навигация по программному обеспечению
  • Глава 4. Ввод и вывод
  • Глава 5. Структуры данных
  • Глава 6. Преобразование данных
  • Глава 7. Строки и даты
  • Глава 8. Вероятность
  • Глава 9. Общая статистика
  • Глава 10. Графики
  • Глава 11. Линейная регрессия и дисперсионный анализ
  • Глава 12. Полезные хитрости
  • Глава 13. За пределами основных цифр и статистики
  • Глава 14. Анализ временных рядов
  • Глава 15. Простое программирование
  • Глава 16. R Markdown и публикации



Заказать книгу на сайте сайт издательства ДМК Пресс
Генеральный дилер ДМК ПРЕСС – КТК Галактика



вторник, 17 декабря 2019 г.

Зарплаты игроков NHL 2019

Есть замечательный сайт с зарплатами игроков NHL - NHL Salary Rankings.
Зарплаты игроков даны в разрезе команд, амплуа, годов. Я скачал данные 2019 года - просто ради прикола и хочу поделиться.

Зарплаты игроков NHL 2019




Здесь по оси X - зарплата в миллионах долларов, по оси Y - количество игроков, которые получают зарплату в таком размере. Т.е. например, зарплату от 5 до 6 миллиона долларов получают 75 игроков. Описательные статистики такие

  • Min.   569355   
  • 1st Qu.  792500   
  • Median     1475000  
  • Mean   2829389  
  • 3rd Qu.     4343750 
  • Max.  16000000

Т.е. 3/4 игроков получают менее 4 343 750 $! Я думал, они там побогаче будут.

В разрезе амплуа

Зарплаты игроков NHL 2019

Здесь также зарплата в миллионах долларов $, прикол в том, что несмотря на видимые различия (медиана зарплаты правого нападающего почти 2 мил $, а левого края - чуть выше 1 миллиона долларов), значимых различий нет! Следовательно, у нас нет данных, чтобы сказать о связи амплуа и уровня зарплаты.

В разрезе команд 

Зарплаты игроков NHL 2019

Самая высокая медиана зарплата в Каролине харрикейнс, но значимость различий выше 0, 05, поэтому мы делаем вывод: если вы хотите играть в NHL, то не выбирайте команду по уровню зарплаты, они все примерно одинаковы.

Код

Если кто-то хочет скачать данные и провести свой анализ, делюсь кодом (а еще лучше приходите на Он-лайн курс "Введение в R для HR")
library(rvest)
team = c('anaheim-ducks' , 'arizona-coyotes', 'boston-bruins', 'buffalo-sabres',
         'calgary-flames', 'carolina-hurricanes', 'chicago-blackhawks', 'colorado-avalanche',
         'columbus-blue-jackets', 'dallas-stars', 'detroit-red-wings', 'edmonton-oilers',
         'florida-panthers', 'los-angeles-kings', 'minnesota-wild', 'montreal-canadiens',
         'nashville-predators', 'new-jersey-devils', 'new-york-islanders', 'new-york-rangers',
         'ottawa-senators', 'philadelphia-flyers', 'pittsburgh-penguins',
         'san-jose-sharks', 'st-louis-blues', 'tampa-bay-lightning', 'toronto-maple-leafs',
         'vancouver-canucks', 'vegas-golden-knights', 'washington-capitals',
         'winnipeg-jets')
y  = NULL;
for(i in team) {
  
  k=html_table(html_nodes(read_html(paste0('https://www.spotrac.com/nhl/rankings/cash/',i,'/')), "table")[[1]], header = TRUE)
  k$team = i
  y = rbind(y, k)
}
y

obj = strsplit(as.character(y$Player), "\n\t\t" ) 
y$Player = sapply(obj, '[', 1)
y$amplua = trimws(lapply(obj, '[', 2))
y$cash = as.numeric(gsub("[^0-9]", "", y$cash))
Кстати, если вы знаете более простой способ скачивания данных - поделитесь! Спасибо!

среда, 21 августа 2019 г.

Как считать текучесть персонала в Power BI на языке R

Это компиляция двух постов:

  1. Как считать текучесть персонала в Power BI / DAX
  2. Как считать текучесть персонала в R/Rstudio (кейс на примере конкретной компании)

И пост не носит практической пользы. Это скорее реклама для изучения R, см. Он-лайн курс "Введение в R для HR"
Основная идея поста: если Вы владеете языком R, у Вас есть преимущество перед теми, кто знает только PowerBI.
Данные я беру из Как считать текучесть персонала в Power BI / DAX, код я беру из Как считать текучесть персонала в R/Rstudio (кейс на примере конкретной компании).

Все остальное в видео.



понедельник, 1 июля 2019 г.

Подстановка значений в data.frame из строки N+1



Небольшой лайфхак. Клиент прислал данные такого типа

df = data.frame(id = c(1,1,1,2,2,2), 
begin=c('01.01.2018', '01.02.2018','01.03.2018', '01.04.2018','01.05.2018', '01.06.2018' ),
end=c('01.01.2018', '01.03.2018','01.04.2018', '01.04.2018','01.06.2018', '01.07.2018'),
category = c('Прием', 'Повышение', 'Увольнение','Прием', 'Повышение', 'Увольнение' ))

  id      begin        end   category
1  1 01.01.2018 01.01.2018      Прием
2  1 01.02.2018 01.03.2018  Повышение
3  1 01.03.2018 01.04.2018 Увольнение
4  2 01.04.2018 01.04.2018      Прием
5  2 01.05.2018 01.06.2018  Повышение
6  2 01.06.2018 01.07.2018 Увольнение 
Toy example. Здесь id - табельный работника, и вы видите, что первые три строки - это все про одного работника. Begin - начало как-то периода. End - конец периода. category, соответственно, описание периода. Так, например, работник один был принят 1 января 2018, уволился 1 апреля 2018.
Проблема данных в том, что в первой строке ячейка end равна ячейке begin. Ну так система велась. А нам надо посчитать продолжительность каждого периода. Для корректного анализа нам надо заменить значение колонки end, которая соответствует category = Прием значением из колонки begin строки, следующей за приемом. Т.е. в первой строке наших данных значение колонки end 01.01.2018 нам необходимо заменить на значение 01.02.2018 колонки begin следующей строки.
И делаем мы это с помощью следующей формулы
df$number = c(1:nrow(df)) 
Создаем новую переменную, которая содержит номера строк. И
df$end1 = ifelse(df$id == df$id[df$number+1] & df$category == 'Прием', as.character(df$begin[df$number+1]), as.character(df$end))
df
  id      begin        end   category number       end1
1  1 01.01.2018 01.01.2018      Прием      1 01.02.2018
2  1 01.02.2018 01.03.2018  Повышение      2 01.03.2018
3  1 01.03.2018 01.04.2018 Увольнение      3 01.04.2018
4  2 01.04.2018 01.04.2018      Прием      4 01.05.2018
5  2 01.05.2018 01.06.2018  Повышение      5 01.06.2018
6  2 01.06.2018 01.07.2018 Увольнение      6 01.07.2018 
Можно было сразу результат записывать в переменную df$end, но я создал переменную end1, чтобы можно было сравнить end и end1.
Обратите внимание, у нас end1 меняется только в тех строках, что соответствуют category = Прием, и меняются на значение переменной begin следующей строки.
That's all.
** и да, дальше переменную begin и end1 надо перевести в формат Даты, посчитать время, но это другая история
Приглашаю записать на Он-лайн курс "Введение в R для HR" и Семинар-практикум "HR-Аналитика в R", Москва, 21-22 ноября
 


Подстановка значений в data.frame из строки N=1


вторник, 21 мая 2019 г.

Как обрабатывать результаты вопросов с множественным выбором в R/Rstudio



Понятно, что я не охвачу все возможные варианты обработки вопросов с множественным выбором, но, надеюсь, хватит, чтобы самим дальше продвигаться.
Показываю на примере поста Популярность джоб сайтов среди соискателей / кандидатов.
У нас есть вопрос, на каких джоб сайтах, вы размещали резюме. Посмотрите это в самом опросе Исследование времени поиска работы (и пройдите его). Результат это вопроса записывается так
Как обрабатывать результаты вопросов с множественным выбором в R/Rstudio



Захватил соседний вопрос, в каких социальных сетях, размещали резюме. Заметно, что результаты сохраняются одной ячейке, и это главное неудобство для обработки.

Преобразование в бинарные переменные

Покажу первые пять строк. После преобразования у нас первые пять строк принимают такой вид
hh
Superjob
Отраслевые
другое
1
1
0
0
1
0
0
0
1
0
1
1
1
1
0
0
1
1
0
0

Т.е. если вы сравните с картинкой выше, вы обратите, что в колонке hh стоят везде единички, потому что hh.ru присутствует во всех первых пяти строках. Superjob содержит единички в тех ячейках, где этот сайт был выбран респондентом и т.п.. 
Такое преобразование позволяет уже пользоваться этими переменными при построении моделей, выявлении различий и т.п...
В R это делается так
df$hh = ifelse(grepl('hh',df$На.каких.работных.сайтах.размещали.резюме), 1, 0)
Этой формулой мы создаем новую переменную. Буквально она звучит так: если у нас в ячейке содержится hh, то в новой переменной значение ячейки будет = 1, в остальных случаях = 0.
Я показал только на примере одной переменной, понятно, что также надо делать с остальными.
Подсчет числа выборов
А если мы хотим посчитать, как часто выбирали респонденты HH, Superjob и т.п.?
И получить вот такую картинку

Картинка обозначает, что 88, 3 % респондента размещали свое резюме на HH.ru и так далее..... В R я это считаю так
HH = grep("hh",df$На.каких.работных.сайтах.размещали.резюме,value=F)
HH =  length(HH)/nrow(df) 
Superjob = grep("Superjob",df$На.каких.работных.сайтах.размещали.резюме,value=F)
Superjob =  length(Superjob)/nrow(df) 
Первой строкой я считаю количество ячеек, содержащих hh, второй строкой я считаю % (делю на количество строк датасета). Если вам не нужны проценты, вы убираете знаменатель и все. Теперь у нас есть два объекта HH и Superjob, нам нужно получить диаграмму (на самом деле у вас будет больше объектов, по числу вариантов ответа, но я просто экономлю пространство, поскольку это просто повторение. И диаграмму я получаю так: объединяю объекты в один датасет
 js = data.frame(value = c(HH, Superjob, Rabota, job, Росработа, avito, otrasl))
col = c('HH', 'Superjob', 'Rabota', 'job', 'Росработа', 'avito', 'otrasl')
df = cbind(col, js)
И теперь визуализируем
df = df[order(-(df$value)),] # задаю в порядке убывания
df

library(ggplot2)
ggplot(df,aes(x = reorder(df$col, df$value),  y = df$value)) + 
  geom_bar(stat="identity", fill="coral1", colour="darkgreen") +
  geom_text(aes(label=paste(round(df$value*100, 1),"%",sep="") ), hjust = 0.5, size=8) + 
  theme(text = element_text(size=30), plot.title = element_text(hjust = .5)) +
  coord_flip()+ labs(title = "% респондентов", y = " ", x='сети')+
  theme(axis.text = element_text(size = 19))
Обратите внимание, я перед загрузкой диаграммы сначала расположил варианты ответа в порядке убывания.
Все, мы посчитали количество выбранных вариантов.

Сравнение групп

А если вы захотели посчитать, как пользователи разных джоб сайтов различаются с т.з. зарплаты, возрасте, уровня позиции и т.п.? Т.е. мы захотим получить, например, вот такую диаграмму
Как обрабатывать результаты вопросов с множественным выбором в R/Rstudio



Т.е. мы сравниваем группы. Мы получили ранее отдельные бинарные переменные, которые позволяют сравнить, например, HH.RU с остальными сайтами сразу всеми вместе, но нам то нужно именно все сайты посмотреть, значим нам нужно получить такой датасет, в котором будут две колонки:

  1. принадлежность к сайту / группе, т.е. переменная будет состоять из значений 'hh', 'Superjob' и т.п..,
  2. зарплата 

В R я делаю это так
 
q$hh = ifelse(grepl("hh",q$На.каких.работных.сайтах.размещали.резюме), '1', '0')
hh = q[q$hh == '1', ]
С помощью первой строки я повторяю, что делал выше - создаю новую переменную, а второй строкой фильтром отбираю только те строки полного датасета, где содержится HH. Далее я с помощью пакет dplyr отбираю зарплаты, создаю из них новый датасет hh, и в нем новую переменную js -она - эта переменная - состоит только из значений hh, т.е. мы выбрали зарплаты только тех респондентов, которые размещали свои резюме на HH.RU
 
library("plyr"); library("dplyr")
hh = select(hh, zp = Размер.зарплаты.на.момент.увольнения)
str(hh)
hh$js = 'hh'
Аналогично я проделываю это с другими сайтами: для Superjob создаю датасет Superjob и так далее
 q$Superjob = ifelse(grepl("Superjob",q$На.каких.работных.сайтах.размещали.резюме), '1', '0')
Superjob = q[q$Superjob == '1', ]
Superjob = select(Superjob, zp = Размер.зарплаты.на.момент.увольнения)
str(Superjob)
Superjob$js = 'Superjob'
Теперь нам надо все это объединить в единый датасет. rbind() нам в помощь, поскольку названия переменных во всех датасетах у нас идентичные.
js = rbind(hh, Superjob, Rabota, otrasl )
js$zp = as.numeric(as.character(js$zp))
js$js = as.factor(js$js) 
Далее мы можем посмотреть значимость различий
kruskal.test(js$zp ~ js$js)
Выяснив, что для наших данных таких различий нет. И построим боксплот
ggplot(js, aes(x=js, y=zp/1000, fill=js)) + geom_boxplot(color='blue')  +
  theme_grey(base_size = 30)+ theme(legend.position="none") +
  scale_y_continuous(breaks=seq(0, 250, 25)) + coord_flip(ylim=c(0, 250)) +
  ylab('зарплата') 
Обратите внимание, что в формуле я использую zp/1000 - т.е. делю зарплату на 1000, чтобы деления не рябили нулями, были не в рублях, а тысячах рублей.

Как обходить засады

Бывают подводные камни. Например, у нам есть джоб сайт job.ru, если мы воспользуемся формулой
job = grep("job",df$На.каких.работных.сайтах.размещали.резюме,value=F)
job =  length(job)/nrow(df)  
То мы соберем в эту переменную помимо job еще и весь Superjob, в котором также есть job. Я преодолеваю эту засаду таким образом.
job = q[!grepl("Super",q$На.каких.работных.сайтах.размещали.резюме) & grepl("job",q$На.каких.работных.сайтах.размещали.резюме), ]
job =  nrow(job)/nrow(q)  
Буквально эта формула означает: мы собираем все, что НЕ содержит Super и содержит job.

Ну вот такой маленький кусочек того, что можно делать с результатами вопросов с множественным выбором. Буду рад дополнениям, или если вы покажете, как это сделать проще. Спасибо!



пятница, 15 марта 2019 г.

Модерация или очищение эффекта в R на примере интроверсии замужних женщин



Очень распространенный вопрос. У специалистов может возникнуть вопрос к посту "Психологические различия дам в официальном и гражданском браке" -на самом ли деле интроверсия - экстраверсия определяет семейный статус дам.
Это к вопросу про "ложные" корреляции и т.п...
Например, среди специалистов, которые указывают сеть ВКонтакте как основную, текучесть выше, чем среди остальных. Виновата ли сеть? Нет. Все просто - эту сеть указывают как основную более молодые специалисты, а более молодые и работают в среднем меньше. В этом случае связь между текучестью и сетью ВКонтакте опосредует возраст, возраст является модератором - таким фактором, который имеет связь и с выбором соц сети, и с текучестью персонала. В общем виде схема такова

  1. X - некий фактор, который имеет корреляцию с Y, и если мы создадим уравнение Y ~ X, то связь будет значима.
  2. M - фактор, который коррелирует с X и Y. И если мы запишем Y ~ M и X ~ M, то в обоих случая связь будет значима. 
  3. Но если мы запишем Y ~ X + M, то значимость X теряется, потому что на самом деле связь есть только у M с Y

Это и есть эффект модерации. Думаю, не нужно сильно погружаться в объяснение важности этого эффекта: как в случае с сетью ВКонтакте: мы могли бы отсеивать тех, кто указывал свою сеть ВКонтакте как основную, и тем самым теряли бы специалистов.
Эта же логика может быть заложена в указанном посте "Психологические различия дам в официальном и гражданском браке" - я показываю, что среди дам в официальном и гражданском браках есть значимые различия по качеству интроверсия - экстраверсия. Но не факт, что это действительно так. Интроверсия - экстраверсия коррелирует с возрастом, а в гражданском браке могут быть просто более молодые женщины, поэтому различие в интроверсия - экстраверсии может быть вызвано не семейным статусом, а возрастом.
Вот это и надо проверять.


Далее код

Данные берем из пакета HRanalitycs 
library(HRanalytics)
data("survey")
Сабсетинг я не буду показывать, если вам, как спецу интересен этот пост, значит вы уже умеете делать это. Я беру полные строки по полу (обратите внимание, там пол указан в разных регистрах), выбираю семейный статус женат и гражданский брак, и полные строки по году рождения. Всего 541 строка. Репрезентативно? Проверим наличие корреляции между возрастом и инстроверсией
cor.test(q1$Год.Рождения, q1$Ш6...ИНТРОВЕРСИЯ...ЭКСТРАВЕРСИЯ.)
 Pearson's product-moment correlation

data:  q1$Год.Рождения and q1$Ш6...ИНТРОВЕРСИЯ...ЭКСТРАВЕРСИЯ.
t = 4.0552, df = 539, p-value = 5.749e-05
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.08905551 0.25269745
sample estimates:
      cor 
0.1720633 
Действительно, слабая (< 0, 3) связь есть. Чем моложе женщина, тем экстравертированней.

Может возникнуть сомнение в нормальности распределения: skew kurtosis для возраста -0.29    -0.12, для интроверсии 0.01    -0.44.
Я знаю три варианта проверки эффекта модерации:

  1. в уравнении регрессии предиктор теряем значимость в присутствии модератора
  2. формула sobel()
  3. SEM - structural equation modeling

Последним я никогда не занимался, а вот первые два метода мы применим. Проблема в том, что у нас аутпут - категориальная переменная, и я честно признаюсь, не знаю, если специальные уравнения для проверки логистической регрессии, Но тем не менее


summary(glm(q1$Семейное.положение.на.момент.трудоустройства ~ q1$Ш6...ИНТРОВЕРСИЯ...ЭКСТРАВЕРСИЯ.+ q1$year, family= 'binomial'))
q1$Ш6...ИНТРОВЕРСИЯ...ЭКСТРАВЕРСИЯ.  -2.0880     0.5396  -3.870 0.000109 ***
q1$year                              -2.8793     0.6573  -4.380 1.18e-05 ***
Показываю уравнение лог регрессии и значимость предикторов. Сами переменные шкалированы в границах 0:1. Значимы обе переменные. Тем не менее, применим формулу sobel(), где зависимая переменная будет as.integer(q1$Семейное.положение.на.момент.трудоустройства)
library(multilevel)
sobel(pred=q1$Ш6...ИНТРОВЕРСИЯ...ЭКСТРАВЕРСИЯ.,med=q1$year,out=q1$Семейное.положение.на.момент.трудоустройства)
У нас q1$Семейное.положение.на.момент.трудоустройства) - это output или зависимая переменная
интроверсия - проверяемая переменная
year - год рождения - медиатор.
Результаты
$`Mod1: Y~X`
             Estimate Std. Error   t value      Pr(>|t|)
(Intercept)  1.967276 0.05170370 38.049047 8.300863e-155
pred        -0.432824 0.09323694 -4.642194  4.334812e-06

$`Mod2: Y~X+M`
              Estimate Std. Error   t value      Pr(>|t|)
(Intercept)  2.2387412 0.07973573 28.077014 1.725524e-107
pred        -0.3620601 0.09306240 -3.890509  1.125716e-04
med         -0.4763393 0.10778717 -4.419258  1.198319e-05

$`Mod3: M~X`
             Estimate Std. Error   t value      Pr(>|t|)
(Intercept) 0.5698981 0.02031517 28.052834 1.948845e-107
pred        0.1485578 0.03663422  4.055165  5.749226e-05

$Indirect.Effect
[1] -0.07076391
Первое, на что обращаем внимание
$Indirect.Effect - сравниваем его со значением 0, 05 - чуть чуть не дотягивает до того, чтобы мы признали наличие эффекта модерации. А счастье было так близко.
Обратите внимание, в уравнении $`Mod2: Y~X+M` значимы также обе переменные.
Поэтому мы делаем, что интроверсия связана с семейным статусом женщины: более интравертированные женщины склонны к официальному браку, экстраверты - к гражданскому.
Модерация или очищение эффекта в R на примере интроверсии замужних женщин
Здесь

  • по оси X - год рожления
  • по оси Y - интроверсия - экстраверсия (ближе к десяти - экстраверты)
  • цвет точек - семейное положение, красные гражданский брак, зеленые - официальный. 

Наклон линии скорее говорит об эффекте модерации, но это уже другая история, может быть потом напишу еще один пост, и эффекта модерации здесь нет:)



вторник, 5 марта 2019 г.

Как считать текучесть персонала в R/Rstudio (кейс на примере конкретной компании)



Это по сути повторение поста Как считать текучесть персонала в excel (кейс на примере конкретной компании) с той лишь разницей, что я считаю текучесть в R / Rstudio.
Я беру тот же самый файл, что ив приведенном посте.
И считаю текучесть я в соответствии с российским законодательством:
число уволенных работников за отчетный период / среднесписочная численность работников за отчетный период.
И среднесписочная считается так:
Среднесписочная численность работников "за отчетный месяц исчисляется путем суммирования численности работников списочного состава за каждый календарный день отчетного месяца, т.е. с 1 по 30 или 31 число (для февраля - по 28 или 29 число), включая праздничные (нерабочие) и выходные дни, и деления полученной суммы на число календарных дней отчетного месяца.
Численность работников списочного состава за выходной или праздничный (нерабочий) день принимается равной списочной численности работников за предшествующий рабочий день. При наличии двух или более выходных или праздничных (нерабочих) дней подряд численность работников списочного состава за каждый из этих дней принимается равной численности работников списочного состава за рабочий день, предшествовавший выходным или праздничным (нерабочим) дням." Консультант плюс

Решение

Если Вы хотите овладеть языком R, рекомендую свой он лайн курс Он-лайн курс "Введение в R для HR"
После загрузки данных, дату поступления и дату увольнения конвертируем в переменную типа Дата.
df$begin = as.Date(df$Дата.поступления, "%d.%m.%Y")
df$end = as.Date(df$дата.увольнения, "%d.%m.%Y")
df$end[is.na(df$end)] =  "2018-05-30 UTC" 
Последняя строка здесь - я заполнил пустые ячейки дат увольнения (пустые, потому что эти работники продолжают работать) датой выгрузки отчета. И формула
n  = NULL;
m = NULL;
for(i in seq(as.Date('2018-01-01'), as.Date('2018-01-31'), by = 'day')) {
  x = length(which(i == df$end))
  m = rbind(m, x)
  y = length(which(df$begin < i))-length(which(i > df$end))
  n  = rbind(n, y)
 }
print(sum(m)/mean(n))
Здесь:
seq(as.Date('2018-01-01'), as.Date('2018-01-31'), by = 'day')
это последовательность дней месяца
m - это количество уволенных в день месяца;
n - среднесписочная за день.
Потом мы делим количество уволенных за месяц на среднесписочную за месяц. У меня за январь 2018 года текучесть получилась 0.01064467. Или 1, 06 %.
В предыдущем посте за тот же период текучесть персонала по формуле в excel получилась 0.010644666 - ну т.е. мы получили одинаковые цифры.
В Rstudio мне кажется считать текучесть удобнее, поскольку не нужно по каждому месяцу рисовать портянку. Задал в seq нужные даты и посчитал за 1,2 секунды. Причем, мы ведь можем считать не только за месяц, но и за два месяца, квартал, год и т.п...
Давайте усовершенствуем
Можно зашить расчеты в формулу
turnover = function(data, date_begin, date_end) {
  n  = NULL;
  m = NULL;
  for(i in seq(as.Date(date_begin), as.Date(date_end), by = 'day')) {
    x = length(which(i == df$end))
    m = rbind(m, x)
    y = length(which(df$begin < i))-length(which(i > df$end))
    n  = rbind(n, y) }
  print(sum(m)/mean(n))
}

И мы можем получить результат, где df - наши данные, первая дата - начала периода, вторая дата - конец периода.
turnover(df, '2018-01-01', '2018-01-31')
[1] 0.01064467


Диаграмма текучести

Но мы могли бы сразу настроить построение диаграммы. Например, я хочу построить график текучести помесячно. Я  создаю новую формулу.
turn_plot = function(data, date_begin, date_end) {
  n  = NULL;
  m = NULL;
  for(i in seq(as.Date(date_begin), as.Date(date_end), by = 'day')) {
    x = length(which(i == df$end))
    m = rbind(m, x)
    y = length(which(df$begin < i))-length(which(i > df$end))
    n  = rbind(n, y) }
  
  a =format(seq(as.Date(date_begin), as.Date(date_end), by = 'day'), '%m.%Y')
  
  gh = data.frame(tapply(m/n,a, sum ))
  gh$date = row.names(gh)
  colnames(gh) = c('num', 'date')
  gh$num = as.numeric(gh$num)
  library(ggplot2)
  library(scales)
  ggplot(data=gh, aes(x=date, y=num)) + geom_bar(stat="identity", color="blue", fill="white") +
    geom_text(aes(label=scales::percent(num, accuracy = 0.001)), vjust=-0.1, size=6.5) +
    theme_grey(base_size = 30) 
  
}
И я задаю даты за 4 месяца
 
turn_plot(df, '2018-01-01', '2018-04-30') # только имейте ввиду, что дата '2018-04-31' выдаст ошибку
И мы получаем
Как считать текучесть персонала в R/Rstudio (кейс на примере конкретной компании)


Из меня не очень хороший дизайнер, но картинку можно нарисовать любую.

 __________________________________________________________
 На этом все, читайте нас в фейсбуке, телеграмме и вконтакте

вторник, 26 февраля 2019 г.

Он-лайн курс "Введение в R для HR"



Семинар Введение в R для HR


ПРИГЛАШАЮ ОТСЛЕЖИВАТЬ НАС В ТЕЛЕГРАМ

Язык R на сегодня является самым популярным инструментом среди HR-аналитиков.
Знание языка R сегодня необходимо также как знание excel. Это признак хорошего тона, это признак развитых аналитических навыков у HR.
Он-лайн курс "Введение в R для HR"  из разряда "с нуля" - для тех, у кого нет никакой базы.
Сам курс я бы назвал "гигиеническим" - неважно, кто Вы - рекрутер, c&b, специалист по оценке персонала - R необходимо знать.

Цели курса

Дать навыки работы с данными и анализа данных в R:
  1. "Готовка" данных (загрузка, объединение, чистка данных);
  2. Визуализация;
  3. Статистический анализ (понимание, в какой ситуации какой критерий применять, какие задачи решать, A/B testing);
  4. Основы предиктивной аналитики (линейная и логистическая регресии). 
Делю понятия работы с данными и анализа данных, поскольку работа с данными более широкое понятие. Курсы по анализу данных на ресурсах типа coursera представляют собой классику по анализу данных, где данные нам даны уже в "причесанном" виде.
В работе HR таких идеальных ситуаций просто не бывает. Данные в HR "грязные". Например, переменная "пол" может содержать значения "м", "ж", "М", "Ж", "муж", "жен", "муж.", "жен." и так далее. Надо все это многообразие обозначений одной командой, быстро привести к "м" и "ж", так, чтобы в последствии не было мучительно больно за бесцельно прожитые годы. Или другая стандартная задача: у вас есть дата приема и дата увольнения (или дата выгрузки отчета), вам нужно посчитать стаж работников и превратить его в отчет.
На курсе мы будем рассматривать шаблоны решения подобных задач.
И после этого посмотрим виды анализа данных. Дойдем до линейной и логистической регрессий.
Начинать курс можно в любое время и обучаться со своей скоростью!


Требования к установке

Необходима установка R и Rstudio
сначала устанавливаем R по ссылке
Далее устанавливаем RStudio Desktop Open Source License (выбираем из списка) 
https://www.rstudio.com/products/rstudio/download/

Ниже видеоинструкция по установке R и Rstudio



Инструкция по установке R / Rstudio от Бориса Демешева

Требования к участникам 

  1. Вы работаете в excel, знаете и понимаете смысл сводных таблиц, формул ЕСЛИ(), ВПР()
  2. Вы знаете основы статистики, понимаете, что означают термины "корреляция", "значимость различий", если нет, то рекомендую купить / скачать книги А.О. Крыштановского "Анализ социологических данных" и А. Д. Наследова "Математические методы психологического исследования"
  3. Английский - желательно от pre - intermediate: семинаром невозможно все охватить, придется много пользоваться поиском в интернете, запросы надо уметь формулировать на английском. Это не так сложно, но требует сноровки


Содержание семинара

После приобретения курса Вы получите подробное описание курса. В состав лекции входит видео (в среднем видео длится около получаса) + скрипт кода лекции + файлы с данными. 
  • Лекция1. Загрузка данных
  • Лекция2. Типы данных, dataframe, cbind(), rbind(), ВПР (merge)
  • Лекция3. Чистка, препроцессинг данных
  • Лекция4. Пропущенные значения
  • Лекция5. Отбор, фильтрация данных
  • Лекция6. Описательные статистики, доверительные интервалы
  • Лекция7. Взаимосвязь количественных переменных
  • Лекция8. Взаимосвязь категориальных переменных
  • Лекция9. Взаимосвязь количественных и категориальных переменных
  • Лекция10. Визуализация с помощью ggplot
  • Лекция11. Работа с форматом даты
  • Лекция12. Линейная, логистическая регрессии

Стоимость семинара для физ лиц

  1. Базовый пакет. 12 видеолекций + 12 скриптов (код лекции) + файлы с данными - 4 600 рублей. Вы получаете 12 ссылок на видео + 12 файлов кодов + подробное описание курса. 
  2. Вопросы для самостоятельной подготовки. 10 вопросов для каждой лекции Х 12 лекций = 120 вопросов. 1200 рублей. Можно консультироваться по данным вопросам, но прямых ответов я давать не буду, буду подсказывать, ответы находите сами
  3. Сертификат. Для получения сертификата необходимо сдать тест. Стоимость теста и сертификат - 700 рублей. 
* возможен доступ к группе техподдержки в социальных сетях 
** для представителей других государств  сумма эквивалентна + возможные затраты на перевод. 
**** для юр лиц индивидуально

ПРИГЛАШАЮ ОТСЛЕЖИВАТЬ НАС В ТЕЛЕГРАМ






пятница, 23 ноября 2018 г.

Задача мультиклассификации с помощью пакета mlr



Задачи машинного обучения, где надо прогнозировать больше, чем два класса, называются задачами мультиклассификации.
Показываю, как я решаю задачу мультиклассификации с помощью пакета mlr.
Нам понадобится датасет survey пакета HRanalytics (установить см. Пакет R HR analytics для HR аналитиков).
Пост посвящаю последнему семинару по HR-аналитике в R, на котором мы не успели рассмотреть задачу мультиклассификации в R

Задача мультиклассификации с помощью пакета mlr

Постановка задачи

Данный датасет представляет ответы респондентов об одном из своих мест работы + некоторые из респондентов проходили тесты на интеллект и Big5 - личностный опросник.
В качестве целевой переменной я беру вопрос "Как часто Ваш руководитель Вам обратную связь" с четырьмя вариантами ответа:
  1. ежедневно;
  2. еженедельно;
  3. реже, чем раз в неделю;
  4. никогда
Я хочу ответить на вопрос, можем ли мы на основе знания о респонденте предсказать, какой вариант ответа об обратной связи от руководителя он выберет. Под знаниями о респонденте я понимаю результаты тестов.
Т.е. можем ли мы сказать, что интеллект, интроверсия и т.п.. определяют, укажет ли респондент давал ли ему руководитель ежедневную связь или не давал никогда.

Код 

Для начала укажем необходимые пакеты
library(mlr)
library(dplyr)
library(data.table)
library(mltools)
library(ROCR)
давайте выберем необходимые переменные
data = dplyr::select(survey, feedback=Как.часто.Ваш.руководитель.давал..дает..Вам.обратную.связь,
       iq=Ш1...ОБЩИЙ.БАЛЛ., intro = Ш6...ИНТРОВЕРСИЯ...ЭКСТРАВЕРСИЯ.,
      impls = Ш8...ИМПУЛЬСИВНОСТЬ...САМОКОНТРОЛЬ.,
      independ = Ш7...НЕЗАВИСИМОСТЬ...СОГЛАСИЕ.) 
У нас много пропущенных значений, поэтому просто оставим полные
data = data[complete.cases(data), ] 
Фишка мультиклассификации в пакете mlr в том, что целевая переменная принимается в one hot encoding и притом еще тип переменной - logical :)
Поэтому я делаю так
data[, c('day', 'everyweek', 'never', 'moreweek')] = one_hot(as.data.table(data$feedback))
data[, c('day', 'everyweek', 'never', 'moreweek')] =lapply(data[, c('day', 'everyweek', 'never', 'moreweek')], as.logical)
Это выглядит так


head(data[, c('day', 'everyweek', 'never', 'moreweek')])
     day everyweek never moreweek
1  FALSE     FALSE  TRUE    FALSE
2  FALSE     FALSE  TRUE    FALSE
15 FALSE      TRUE FALSE    FALSE
16 FALSE      TRUE FALSE    FALSE
24 FALSE     FALSE  TRUE    FALSE
25 FALSE      TRUE FALSE    FALSE 
Далее приводим данные в необходимый формат mlr
data = data[, -1]
labels = c('day', 'everyweek', 'never', 'moreweek')
data.task = makeMultilabelTask(id = "multi", data = data[, -1], target = labels)
Обратите внимание, я беру data[, -1], потому что первая переменная эта наша "сырая" целевая переменная,с ней наш прогноз будет очень точным:) Хотя он и так будет точным, потому что я не буду разбивать на трейн / тест сплит, думаю, вы это сделаете сами. Далее указываем учителя
mrf = makeLearner("multilabel.randomForestSRC",  predict.type = "prob")
getParamSet("multilabel.randomForestSRC") 
Обращаю внимание, ниже я даю ссылку на источник, там вы можете увидеть вариант, как можно решать задачи мультиклассификации в mlr с помощью учителя бинарного классификатора. Задаем параметры (я даю мало, вы же сами выбираете то, что вам нра.
mrf_param = makeParamSet(
  
  makeIntegerParam("ntree",lower = 50, upper = 500),
  makeIntegerParam("mtry", lower = 1, upper = 6)
  
) 
Далее обычные операции подготовки данных
rancontrol = makeTuneControlRandom(maxit = 7L) # мы ограничимся 5 итерациями, вы пробуйте больше от 50  
set_cv = makeResampleDesc("CV", stratify = FALSE, iters = 3) 
А здесь важно обратить вминание, что stratify = TRUE не работает, и методы кросс валидации мультиклассификации в mlr отличаются от просто классификации и регрессии (см. ниже по ссылке).
Тренируем
rf_tune = tuneParams(learner = mrf, resampling = set_cv, task = data.task, 
                      par.set = mrf_param, control = rancontrol) 
mrf.tree = setHyperPars(mrf, par.vals = rf_tune$x)
mrforest = mlr::train(mrf.tree, data.task) 
getLearnerModel(mrforest)
Далее я хочу получить метрики качества модели, но поскольку я не разбивал на трейн и тест сеты, то использую полный набор данных.


mrfmodel = predict(mrforest, data.task)
head(mrfmodel$data)
getMultilabelBinaryPerformances(mrfmodel, measures = list(acc, mmce, auc))
          acc.test.mean mmce.test.mean auc.test.mean
day           0.9486964     0.05130362     0.9882470
everyweek     0.9276703     0.07232969     0.9821936
never         0.9520606     0.04793944     0.9902949
moreweek      0.9091674     0.09083263     0.9787145
Вам главный вопрос: почему мы получили такие хорошие метрики качества? Нарисую ROC AUC для вариант day - площадь под кривой того, что руководитель будет давать обратную связь каждый день
 
ROCRpred1 = prediction(mrfmodel$data$prob.day , mrfmodel$data$truth.day  ) #  as.integer(data$day)
as.numeric(ROCR::performance(ROCRpred1, "auc")@y.values)
ROCRperf1 = ROCR::performance(ROCRpred1, "tpr", "fpr")
par(cex.axis=1, cex=1) 
plot(ROCRperf1 , colorize=TRUE, lwd = 6, print.cutoffs.at=seq(0,1,by=0.1), text.adj=c(-0.2,1.7), cex.axis=2)
grid(nx=NULL, ny=NULL, lwd = 3)
lines(x = c(0,1), y = c(0,1), lty=5)
Картинка такая


Задача мультиклассификации с помощью пакета mlr

Это все, что я хотел показать. 

Ссылки

При подготовке я пользовался материалом Multilabel Classification with R Package mlr Также обращаю внимание, я не смог показать фиче импортанс переменных, у меня выдает ошибку. На Гитхабе есть соответствующий коммит см. Feature importance multi-label classification. Я оставил там свой коммент, но сам коммит был оставлен 28 февраля этого года, поэтому сами оценивайте шансы получить ответ:) Можно пойти другим путем, может быть я напишу про это отдельный пост.
Или если вы знаете ответ, напишите, буду вам благодарен.
__________________________________________________________
На этом все, читайте нас в фейсбукетелеграмме и вконтакте

среда, 21 ноября 2018 г.

Какой статистический критерий в какой ситуации использовать



После семинара "HR-Аналитика в R" я решил таки сделать шпаргалку / алгоритм, в какой ситуации какой статистический критерий использовать. При этом помимо описания алгоритма, буду использовать формулы в R.
Статистический критерий применяется для ответа на вопрос о наличии или силе связи (корреляции) между двумя переменными.
Первый и самый важный шаг в использовании статистических критериев - определить, какого типа переменные участвуют в нашем анализе.
Мы поделим переменные на два типа: количественные и категориальные.
Первый тип в R обозначается через numeric или integer, а второй через factor. Таким образом, у нас может быть всего три ситуации применения статистических критериев:
  1. обе переменные количественные
  2. обе переменные категориальные
  3. одна переменная количественная, другая категориальная
И за этим стоит много нюансов, некоторые из которых я раскрываю. Даю формы визуализации.  Рассмотрим все три ситуации (напомню, что это не лекция по статистике, а всего лишь шпаргалка / напоминалка)
Иакже рекомендую видео



1. Количественная - количественная

Шаг первый: выясняет распределение переменных (чаще всего  с помощью теста Шапиро Уилкса
shapiro.test()

Нормальное распределение

Если обе переменные показали по тесту Шапиро Уилска p-value > 0, 05 (например, если мы захотели измерить связь между тестом интеллекта и возрастом), то можем смело применять коэффициент корреляции Пирсона, который в R применяется с формулой
cor.test(x, y, method = 'pearson')
R может нам выдать что-то типа
Pearson's product-moment correlation
data:  x and y
t = 4.5451, df = 79, p-value = 1.95e-05
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
     0.2630944 0.6127261
sample estimates:
     cor
0.455289
p-value у нас 1.95e-05, это меньше 0, 05, что говорит о наличии связи, а сила связи 0, 455289, что говорит о средней силе связи (связь считается сильной, если показатель связи > 0, 5, слабой, если < 0, 1)

Распределение отличается от нормального

Если тест Шапиро Уилска показал p-value < 0, 5 (например, мы измеряем связь между общей удовлетворенностью работы в компании и удовлетворенностью отношениями с руководителем), мы применяем коэффициент корреляции Спирмена
cor.test(x, y, method = ' spearman')

Визуализация

Для визуализации связи используем точечную диаграмму.
Какой статистический критерий в какой ситуации использовать



2. Категориальная категориальная

Если все переменные категориальные (например, в ситуации, когда мы хотим посмотреть связь между тем, к какой категории отнесли работников на ассессмент центре и к какой категории эффективности их отнесли по перформанс ревью), мы чаще всего сначала смотрим на распределение переменных с помощью формулы
table()
Или с помощью
prop.table()
Если мы хотим посмотреть распределение в %. Отвечаем на вопрос о наличии связи с помощью критерия Хи-квадрат.
chisq.test() 
И на вопрос о силе связи мы отвечаем с помощью критерия Крамера
library(lsr)
cramersV() 

Визуализация

Используем для визуализации нормированную гистограмму и / или мозаичную диаграмму.
Какой статистический критерий в какой ситуации использовать



3. Количественная категориальная 

Чаще всего это ситуация, когда мы сравниваем распределение количественной переменной по группам. Например, если мы хотим оценить влияние обучения и сравниваем продажи ДО и ПОСЛЕ обучения, или ели мы сравниваем продажи прошедших и не прошедших обучение. Или если мы сравниваем зарплаты по регионам.
Первый шаг - определение, является ли распределение ли количественной переменной нормальным или нет. Делаем это мы с помощью уже упомянутого критерия Шапиро Уилкса, причем по каждой группе.

воскресенье, 30 сентября 2018 г.

Связь между интеллектом и временем прохождения теста




Достаточно очевидная гипотеза: есть ли связь между интеллектом и временем прохождения теста. Я постараюсь сделать пост так, чтобы он был интересен и тем, кто работает в R, и тем, кого просто результаты интересуют. Т.е. я буду показывать как диаграммы, так и код.
Если Вы работаете в R, у вас должны быть установлены два пакета:

"devtools"
"HRanalytics"
Установить их можно так, как указано по ссылке Пакет R HR analytics для HR аналитиков. В пакете HRanalytics собраны часть результатов нашего исследования факторов эффективности и текучести персонала, и, если кто-то помнит, респонденты могли пройти тесты Лаборатории Гуманитарные Технологии:
  1. тест способностей КТО
  2. личностный опросник Big5
Отчет по тестированию включал помимо результатов теста время прохождения теста в таком формате

0:28:38 
0:27:40 
0:28:51 
0:21:30
Т.е. у нас сразу встает задача перевести данный формат в удобоваримый. Давайте по порядку.
Загружаем необходимые пакеты
library(dplyr)э
library(HRanalytics)
data(survey)
str(survey)
В файле survey куча ненужных нам переменных, поэтому мы формируем отдельный файл в помощью команды select пакета dplyr
tests1 =select(survey, overall=Ш1...ОБЩИЙ.БАЛЛ., time=ПРОДОЛЖИТЕЛЬНОСТЬ )
tests1 = tests1[complete.cases(tests1$time),]
Вторая команда нужна, чтобы убрать много пустых строк: у нас далеко не все респонденты проходили тесты. Итого у нас 1 208 строк, что достаточно для исследования. Теперь самое интересное: давайте переведем наш формат времени в минуты.
obj = strsplit(as.character(tests1$time), ":") # делим текст на объекты по запятой
obj[1:5] # посмотрим, что представляет из себя объект
tests1$часы = as.integer(lapply(obj, '[', 1)) # превращаем последовательно объект в часы, минуты и секунды
tests1$минуты = as.integer(lapply(obj, '[', 2)) 
tests1$секунды = as.integer(lapply(obj, '[', 3)) 

tests1$времятеста = tests1$часы*60+tests1$минуты+tests1$секунды/60 # а теперь сведем в одну переменную 
tests1$времятеста = round(tests1$времятеста, 1) # ограничим одним знаком # ну и для марафета ограничимся одним знаком после запятой
И посмотрим, что из себя время прохождения теста представляет
summary(tests1$времятеста)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   3.60   22.37   25.45   35.47   28.90 1092.00 
Диаграмма
hist(tests1$времятеста)
Связь между интеллектом и временем прохождения теста

Не очень интересная картина: видите справа свыше 1000 минут кто-то проходил? Это явно либо баг системы, либо просто респонденты оставляли тесты незавершенными, уходили куда-то, не выйдя из системы. Поэтому я с потолка беру идею: все, что больше часа по прохождению, означает, что респондент отвлекался от прохождения теста. Обрезаю по 60 минут и заново строю диаграмму
tests2 = subset(tests1, tests1$времятеста <= 60)
hist(tests2$времятеста)
summary(tests2$времятеста)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   3.60   22.20   25.40   25.97   28.60   59.10 
Эти результаты кажутся более осмысленным, хотя стоило бы еще как-то обрезать слева: ну нереально пройти батарею тестов за 3.6 минуты, согласны?
Связь между интеллектом и временем прохождения теста



Распределение кажется нормальным, но давайте все-таки проверим распределение на нормальность с помощью теста Шапиро-Уилка
shapiro.test(tests2$времятеста)

	Shapiro-Wilk normality test

data:  tests2$времятеста
W = 0.92625, p-value < 2.2e-16

> shapiro.test(tests2$overall)

	Shapiro-Wilk normality test

data:  tests2$overall
W = 0.99435, p-value = 0.0002018
P-value менее 0, 05 говорит нам о том, что распределение обоих переменных распределено ненормально, значит для проверки гипотезы о наличии связи между временем прохождения теста и интеллектом мы должны применять не коэффициент корреляции Пирсона, а непараметрический - Спирмена.
hist(tests2$overall)
А на диаграмме распределение ответов по шкале общих способностей теста КТО кажется вполне нормальным
Связь между интеллектом и временем прохождения теста

Давайте теперь проверим гипотезу о наличии взаимосвязи между интеллектом и временем прохождения теста с помощью коэффициента корреляции Спирмена
cor.test(tests2$времятеста, tests2$overall, method = c("spearman") )

	Spearman's rank correlation rho

data:  tests2$времятеста and tests2$overall
S = 317090000, p-value = 8.552e-09
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
-0.166812 
p-value = 8.552e-09 и означает, что связь есть, -0.166812 связь не очень тесная, но она есть и она отрицательная. Последнее выглядит логичным: чем больше время прохождения теста, тем меньше интеллект.
Визуализируем

Связь между интеллектом и временем прохождения теста


По оси X время прохождения теста в минутах, по оси Y - результаты теста КТО, шкала общих способностей. Для очистки совести я убрал тех, кто проходил тесты менее 15 минут (предполагая, что эти респонденты просто бросали тест, не выполняя его до конца)
> tests3 = subset(tests2,  tests2$времятеста >= 15)
> cor.test(tests3$времятеста, tests3$overall, method = c("spearman") )

	Spearman's rank correlation rho

data:  tests3$времятеста and tests3$overall
S = 310710000, p-value = 2.575e-13
alternative hypothesis: true rho is not equal to 0
sample estimates:
       rho 
-0.2130846 
P-value и коэффициент корреляции показывают более высокую связь, чем в предыдущем случае. Таким образом, мы можем говорить о наличии связи между временем прохождения теста и интеллектом (шкалой общих способностей теста КТО).

__________________________________________________________

На этом все, читайте нас в фейсбуке, телеграмме и вконтакте