Обзор проектов Kickstarter - Простое исследование данных на Python
В данной статье анализируется набор данных из примерно 380 000 проектов Kickstarter. Я проведу вас через простое исследование данных с помощью Python, чтобы раскрыть интересную информацию о проектах Kickstarter и о том, какие атрибуты важны, когда дело доходит до проверки успеха (или неудачи) определенного проекта. В общем, мы рассмотрим базовую визуализацию данных и разработку функций с помощью Python.
1. Общие идеи и мысли
Kickstarter - одна из самых известных платформ для продвижения (в основном) творческих, умных и дальновидных идей и концепций. Каждый проект ищет финансирование, которое Kickstarter-Crowd может предоставить, если они поддержат идею и хотят, чтобы она увенчалась успехом. Однако есть много проектов, которые терпят неудачу, даже если первоначальная идея могла быть убедительной. Это почему? В этой статье делается попытка углубиться в атрибуты, связанные с каждым проектом, и выявить закономерности, идеи и все интересное, связанное с проектами Kickstarter.
2. Исходный набор данных и атрибуты
Набор данных содержит 378.661 проект с Kickstarter и двенадцать начальных атрибутов, связанных с каждым проектом. Давайте посмотрим, какую информацию мы получили о каждом проекте:
- name
Совершенно очевидно, название соответствующего проекта. Например. «Ежедневный кофе» - main_category
основная категория, к которой относится проект. Например, «Поэзия», «Еда», «Музыка» и многое другое. - категория
более точное описание основной категории. По сути, это подгруппа main_category (см. 2.). Например. «Напитки», которые будут подгруппой «Еда» из атрибута main_category. - валюта
валюта проекта (например, доллар США или фунт стерлингов) - запущен
срок запуска проекта. Это будет важно при дальнейшем анализе таймфреймов. - крайний срок
крайний срок выполнения проекта. Как и дата запуска, крайний срок станет важным, так же как и дата запуска. - usd_pledged_real
сумма в долларах США, реализованная в установленный срок. - usd_goal_real
сумма в долларах США, которую изначально запрашивал проект - спонсоров
количество сторонников, которые фактически инвестировали в проект. - страна
страна происхождения проекта - ID
идентификатор проекта - состояние
Был ли проект успешным в конце концов? состояние - это категориальная переменная, разделенная на уровни успешный, неудачный, активный, отмененный, неопределенный и приостановленный. Для ясности мы будем смотреть только на то, был ли проект успешным или неудачным (следовательно, мы удалим все проекты, которые не относятся к одному из двух). . Проекты, которые потерпели неудачу или были успешными, составляют около 88% всех проектов.
Давайте быстро удалим все проекты, в состоянии которых нет неудачных или успешных.
#return only successful and failed projects. This makes things more clear later on data_kick = data_kick.loc[data_kick['state'].isin( ['successful', 'failed'])]
Давайте сначала посмотрим на типы данных всех атрибутов, чтобы лучше понять данные и посмотреть, все ли в порядке.
data_kick.info() ID 378661 non-null int64 name 378657 non-null object category 378661 non-null object main_category 378661 non-null object currency 378661 non-null object deadline 378661 non-null object goal 378661 non-null float64 launched 378661 non-null object pledged 378661 non-null float64 state 378661 non-null object backers 378661 non-null int64 country 378661 non-null object usd pledged 374864 non-null float64 usd_pledged_real 378661 non-null float64 usd_goal_real 378661 non-null float64
Видно, что все атрибуты имеют тенденцию быть в точном формате данных - либо с плавающей точкой (отсюда числовой), либо с объектами (отсюда категориальной). Единственные два атрибута, которые не имеют желаемого формата, - это два наших атрибута времени: запущен и крайний срок. Мы позаботимся об этом чуть позже. Но давайте сначала разберемся с набором данных, проанализировав объявленную сумму и исходная цель проекта.
3. Цель и обещание
Давайте посмотрим на взаимосвязь между целью проекта и фактической обещанной суммой. Совершенно очевидно, что проект помечается как успешный, если
объявленная сумма ≥ цели и неудачный, если объявленная сумма ‹ Цель.
На следующем графике показаны как цель, так и сумма, выделенная для каждого проекта, а также индивидуальное состояние проекта (отображаются только неудачные и успешные проекты. ).
#define colors (darkgreen for successful projects and darkred for failed ones colors = ('darkgreen','darkred') #create a plot using seaborn, adjust data to millions ax = sns.scatterplot(data_kick.usd_pledged_real/1e6, data_kick.usd_goal_real/1e6, hue=data_kick.state, palette=colors) #add blue line to better visualize the border between failed and successful projects sns.lineplot(x=(0,50), y=(0,50), color='darkblue') #set the axes from -1 to their maximum (-1 looks better than 0 actually) ax.set(ylim=(-1,None), xlim=(-1,None)) #set labels and title ax.set(xlabel='Amount Pledged in Millions', ylabel='Goal in Millions', title= 'Goal vs. Pledged')
Интуитивно понятно, что проекты, которые обещали больше / меньше своей цели, являются успешными и отображаются зеленым цветом, а неудачные проекты отмечены красным. Посмотрите на синюю линию, пересекающую начало координат: успешные проекты всегда находятся под линией, а неудачные проекты - над ней. В этом есть смысл, не так ли? Между прочим, вы можете заметить, что проект с наибольшей объявленной суммой собрал около 20 миллионов долларов США, а проект с наивысшей целью искал более 160 миллионов долларов США (и потерпел неудачу). Пора присмотреться.
Давайте немного увеличим масштаб, чтобы лучше понять, изменив обе оси на диапазон от нуля до 20 миллионов:
#we are using the same code as above expect for the axis limits, that are set from 0 Millions (-1 in the code) to 20 Millions ax.set(ylim=(-1,20), xlim=(-1,20)
Такое увеличение позволяет использовать более интуитивный подход: синяя линия теперь пересекает начало координат под углом 45 °, поскольку мы настроили оси в соответствии с тем же диапазоном. Как упоминалось ранее, каждая точка данных (следовательно, проект) под линией была успешной, тогда как каждая точка данных над ней не прошла.
Интересно, что график показывает, что неудачные проекты обычно терпят неудачу, даже не приближаясь к своей цели, что означает, что они «не двигаются горизонтально к синей линии, а остаются на уровне x ~ 0». Это приводит к выводу, что Kickstarter - это платформа «все или ничего». Если вы этого не сделаете, вы, вероятно, даже близко не подошли. С другой стороны, многие успешные проекты намного превышают заявленную цель и обещают многократно превышающую первоначальную цель.
Как со временем менялась связь между обещанной суммой и целью? Давайте посмотрим на развитие целей и обещаний за весь период времени, сгруппированные по годам. Мы используем следующий код, чтобы получить накопленную сумму для целей и объявленных взносов за каждый год.
#we choose the desired grouping intervall ('launched year') and the two variables pledge and goal that we are interested in and sum them up over the years. data_kick_groupby_year = data_kick.groupby('launched_year')['usd_pledged_real', 'usd_goal_real'].sum() #let's use this variable to plot the data over time: ax = sns.lineplot(x=data_kick_groupby_year_sum.index, y= data_kick_groupby_year_sum.usd_pledged_real/1e9, linewidth= 4, label= 'USD Pledged', color= 'darkgreen') sns.lineplot(x=data_kick_groupby_year_sum.index, y= data_kick_groupby_year_sum.usd_goal_real/1e9, linewidth= 4,label='USD Goal', color= 'darkred') #set labels and title ax.set(xlabel='Launch Year', ylabel='USD in Billion', title='Comparison Pledged vs Goal over Time')
Получаем этот график:
На графике показаны как накопленные цели, так и объявленные взносы по всем проектам за соответствующий год. Например, в 2013 году сумма всех финансовых целей всех запущенных проектов была немногим выше 1 миллиарда долларов.
Видно, что накопленная сумма общей цели резко возрастает в 2013 году и достигает своего пика в 2015 году, после чего начинает медленно снижаться. С другой стороны, накопленная сумма залога за каждый год неуклонно уменьшается, но довольно линейно. Фактически он немного снизился с 2015 по 2017 год.
Причина резкого увеличения стоимости гола очевидна, не правда ли? Количество проектов резко увеличилось, что можно увидеть здесь:
data_kick['launched_year'].value_counts() 2015 65272 2014 59306 2016 49292 2017 43419 2013 41101 2012 38478 2011 24048 2010 9577 2009 1179
Больше проектов, больше целей. Однако это лишь одна составляющая, способствующая увеличению накопленных целей. С годами не только запускалось больше проектов, но и средняя цель каждого проекта с годами также увеличивалась, как показано здесь:
#we group again. This time we take the average though: data_kick_groupby_year_avg = data_kick.groupby('launched_year')['usd_pledged_real', 'usd_goal_real'].mean() print(data_kick_groupby_year_avg) launched_year usd_pledged_real usd_goal_real 2009 2375.535335 6799.507430 2010 3041.302897 13270.062647 2011 4256.161398 9715.957062 2012 8247.532875 17862.738838 2013 11326.171820 24946.164929 2014 8466.032884 47346.942048 2015 10058.201943 70094.513735 2016 12847.626664 52879.135752 2017 13564.594251 39073.175276
Средняя цель со временем увеличивается (с примерно 10 000 долларов США в 2011 году до примерно 53 000 долларов США в 2016 году с последующим резким падением в 2017 году). Следовательно, увеличилось не только количество проектов, но и средняя цель каждого проекта. Сочетание этих факторов увеличивало график накопленных целей с течением времени. Теперь достаточно времени. Посмотрим на имена!
4. Роль имен
Еще один аспект, который может повлиять на результат проекта, - это его название. Мы ожидаем, что более короткие и лаконичные имена будут более привлекательными для (потенциальных) инвесторов, чем длинные и расплывчатые фразы.
Например, такое название, как «Circa Vitae запрашивает краудфандинг для записи нашего полноформатного альбома… TO VINYL !!!» менее запоминающийся, чем «День Икс» (первый проект действительно провалился, второй - удачно).
Подтверждают ли данные эту гипотезу? Давайте немного поработаем над особенностями и создадим новый столбец, содержащий длину строки названия проекта.
data_kick['name_length'] = data_kick['name'].str.len()
Теперь мы сохранили длину строки имени каждого проекта в name_length. Давайте посмотрим, как выглядит столбец.
data_kick.loc[:,['name','name_length']].head(5) name name_length 0 The Songs of Adelaide & Abullah 31.0 1 Greeting From Earth: ZGAC Arts Capsule For ET 45.0 2 Where is Hank? 14.0 3 ToshiCapital Rekordz Needs Help to Complete Album 49.0 4 Community Film Project: The Art of Neighborhoo... 58.0
Давайте создадим диаграмму рассеяния, чтобы показать взаимосвязь между name_length и суммой usd_pledged_real.
#create scatterplot for pledged amount and name length ax = sns.scatterplot(data_kick.usd_pledged_real/1e6, data_kick.name_length, hue=data_kick.state, palette=colors) #set labels accordingly ax.set(xlabel='Amount Pledged in Millions', ylabel='Character Length of Project Name', title= 'The Importance of Choosing the Right Name')
Интересно, что если название проекта превышает 60 символов, обещанная сумма распределяется около 0 и (более или менее) все проекты кажутся провальными. Давайте посмотрим на цель этих проектов в сравнении с ‹60 проектами и проверим следующую гипотезу:
- Может ли быть, что ›проекты с 60 персонажами имеют явно высокие цели, которые они стремятся собрать? Чтобы проверить эту гипотезу, давайте посмотрим на среднюю цель для name_length ≥60 по сравнению с target для name_length ‹60.
Давайте посмотрим на эту подгруппу проектов, которые содержат более 60 символов с точки зрения их целевого размера и состояния проекта:
#create a variable for all data points above a name length of 60 data_kick_60_up = data_kick.loc[data_kick.name_length >= 60, ['state','usd_goal_real']] #create a variable for the data points below a name length of 60 data_kick_60_down = data_kick.loc[data_kick.name_length < 60, ['state','usd_goal_real']] #compare the median of both groups. We see that the above 60 group actually has a higher median than the below 60 group. print(data_kick_60_up['usd_goal_real'].median() > data_kick_60_down['usd_goal_real'].median()) True
Мы можем видеть, что медиана (а также среднее значение) выше для группы ≥60 относительно их цели (логическое значение возвращает «Истина»). Следовательно, мы не можем сделать вывод, что проекты с длиной имени ≥60 просто имеют более низкие значения target. Напротив, у них на самом деле более высокие цели по сравнению с проектами, имена которых состоят из
Как упоминалось ранее, наиболее интуитивным объяснением «сегментации успеха», основанной на длине названия проекта, является то, что длинные имена имеют тенденцию звучать непрофессионально, не по делу и не так строго (очевидно, это просто личное мнение). Следовательно, они обещают меньше и терпят поражение.
5. Изучение времени
Как упоминалось ранее, все переменные относятся к правильному типу - за исключением временной (ых) переменной (ов). К счастью, Pandas позволяет нам быстро заменять переменные даты на фактические даты:
data_kick['launched'] = pd.to_datetime(data_kick['launched'], format='%Y-%m-%d %H:%M:%S') data_kick['deadline'] = pd.to_datetime(data_kick['deadline'], format='%Y-%m-%d %H:%M:%S')
Если вы сейчас отметите data_kick.info (), вы увидите, что запущен и крайний срок имеют правильный тип данных. Теперь, когда мы преобразовали временные переменные в правильный формат, давайте подробнее рассмотрим их. В частности, у нас есть два интересных столбца времени: точная дата, когда проект был запущен, и, во-вторых, крайний срок для каждого проекта. Что теперь?
Возможно, дата запуска проекта влияет на его успех, возможно, время между запуском и крайним сроком также влияет на результат проекта. Можно утверждать, что чем больше промежуток между запуском и дедлайном, тем больше у вас времени, чтобы быть замеченным и собрать деньги. Следовательно, чем больше продолжительность, тем выше шансы на успех. Кроме того, месяц (или даже час) запуска проекта может повлиять на его результат. Но подтверждают ли данные эти гипотезы? Давайте сначала посмотрим на время запуска.
Мы можем извлечь соответствующий час, когда проект был запущен, с помощью следующего кода:
data_kick['launched_hour'] = data_kick.launched.apply(lambda x: x.hour) data_kick['launched_month'] = data_kick.launched.apply(lambda x: x.month)
Мы только что создали две новые колонки, содержащие месяц и час запуска проекта соответственно. Давайте посмотрим, содержат ли эти новые атрибуты шаблоны, которые помогают нам понять, успешен ли проект или нет. Представим себе час дня и успех проекта:
#extract launched hour and state for all states 'successful' and append the same for all states 'failed'. data_kick = data_kick.loc[data_kick.state == 'successful',['launched_hour', 'state']] .append(data_kick.loc[data_kick.state == 'failed', ['launched_hour', 'state']]) #plot the data ax = sns.countplot(x=extracted_levels_hour.launched_hour, hue = extracted_levels_hour.state) #set labels and title ax.set(xlabel='Launched Hour', ylabel='Number of Projects', title= 'Hour the Project was Launched')
Здесь есть несколько интересных вещей, на которые стоит обратить внимание:
- В целом, больше проектов запускается рано утром (с 0 до 6 часов утра) и больше проектов запускается поздно днем / вечером (с 16 до 23 часов) по сравнению с временем между 7 и 15 часами.
- Соотношение успешных и неудачных проектов меняется в зависимости от времени суток. Мы видим, что соотношение неудачных и успешных проектов составляет почти 1 (на самом деле немного меньше) для проектов, запущенных между 14 и 15 часами. Напротив, соотношение для проектов, запущенных после 20:00, намного хуже (иными словами: неудачных проектов намного больше, чем успешных).
Посмотрим, говорит ли месяц запуска проекта что-нибудь о его состоянии:
#plot the data sns.countplot(x=data_kick.launched_hour, hue = data_kick.state) #set labels and title ax.set(xlabel='Month of the Year', ylabel='Number of Projects', title= 'Month the Project was Launched')
По сравнению с часом дня месяцы, кажется, не дают никаких удивительных открытий. Однако обратите внимание, что в декабре запускается намного меньше проектов по сравнению с остальными месяцами.
Наконец, давайте посмотрим на промежуток времени между запуском и крайним сроком. Мы можем легко получить этот диапазон, вычитая запущенное из крайнего срока (это полезная функция преобразования date_time, которое мы сделали ранее). В приведенном ниже коде мы берем разницу между запуском и крайним сроком и сразу же извлекаем дни («timedelta64 [D]»):
data_kick['project_time'] = (data_kick['deadline'] -data_kick['launched']).astype('timedelta64[D]').astype(int) data_kick['project_time'].head(5) 0 58 1 59 2 44 3 29 4 55
Как видите, в нашем новом столбце project_time измеряется промежуток времени между запуском и дедлайном в днях. Например, первый проект в наборе данных ([0]) находился на Kickstarter почти два месяца (58 дней). Прежде чем визуализировать диапазон для проекта, давайте объединим результаты. Максимальное значение data_kick [‘project_time’] составляет 91, поэтому вместо получения 91 различных результатов мы могли бы объединить данные по неделям, чтобы получить более интуитивный обзор. Давайте сначала напишем функцию, которая позволяет нам эффективно объединять данные:
def discretizer(data, binning_values, labels): """ Input: data series that should be discretized bininningvalues: enter numerical array of binning cuts labels: enter string array of names for bins Output: gives you a list of binned data """ data = pd.cut(data, bins=binning_values, labels=labels) return data
Давайте проанализируем в project_time и отсортируем по неделям:
#save the binned data as new column data_kick["project_time_weeks"] = #call the discretizer funtion discretizer( #first thing we pass is the column we want to be discretized, project_time in this case data_kick['project_time'], #next, we create a list with the different binning intervals (starting at 0 and then going in steps of 7 until the maximum value of project_time (+7) list(range(0,max(data_kick['project_time'])+7,7)), #last but not least, we need to label the bins. We number them by creating a list from 1 (first week) to the last week depending on the maximum value of project_time list(range(1,max(list(range(0,max(data_kick['project_time'])//7+2))))))
Так как же выглядит новый столбец project_time_weeks?
#print first 5 rows of both columns: data_kick[['project_time', 'project_time_weeks']].head(5) #resulting in: project_time project_time_weeks 34 5 19 3 29 5 27 4 14 2
Мы видим, что дни успешно разделены на недели. Например. 19 дней превращаются в неделю 3. Теперь, наконец, давайте посмотрим, повлияет ли время между запуском и крайним сроком на успех проекта:
#plot the data sns.countplot(x=data_kick.project_time_discret, hue = data_reduced.state, palette=colors)
Мы сразу замечаем, что большинство проектов размещаются на Kickstarter примерно на 5 недель, а почти все проекты работают от 1 до 9 недель (то есть чуть больше 2 месяцев).
Соотношение между успешными и неудачными проектами со временем меняется: проекты с коротким сроком выполнения (1–4 недели) достигают успеха относительно чаще, чем проекты, которые запускаются со средним или долгим сроком выполнения (5–13 недель). Это понимание может поддержать гипотезу о том, что вероятность успеха проекта тем меньше, чем дольше они живут, как можно было предположить заранее (- ›чем дольше живет проект, тем больше людей имеет шанс увидеть его с течением времени, тем больше вероятность он будет успешным. Это не похоже на правду).
6. Роль спонсоров
Теперь посмотрим на роль спонсоров, поддерживающих каждый проект. В целом, мы ожидаем, что большое количество спонсоров положительно повлияет на вероятность успеха конкретного проекта. Но так ли это на самом деле?
Давайте визуализируем количество спонсоров, сумму залога и состояние проекта.
ax = sns.scatterplot(x=data_kick.backers, y=data_kick.usd_pledged_real/1e6, hue=data_kick.state, palette=colors) #we set the xlim to 100,000 backers to get a better overview ax.set(ylim=(-1,None), xlim=(-1,100000)) #set labels and title ax.set(xlabel='Number of Backers', ylabel='USD Pledged in Million', title= 'Backer vs Pledge')
Код приводит к следующему графику:
Такой результат неудивителен, правда? Мы ясно видим, что количество спонсоров коррелирует с успехом проекта. Большинство проектов, которые терпят неудачу, также имеют (относительно) меньшее количество спонсоров. Однако давайте пойдем дальше: интересно не только абсолютное количество спонсоров на проект, но и средняя сумма, которую каждый спонсор тратит на проект. Чтобы получить этот атрибут, нам нужно разделить обещанную сумму на количество реальных спонсоров. Давай сделаем это быстро.
#divide usd_pledged_real by number of backers (note that a Zerodivisionerror will occur if we divide by zero resulting in a NaN. We replace these with 0 data_kick['backer_ratio'] = (data_kick['usd_pledged_real']//data_kick['backers']).fillna(0) #set label and title ax.set(xlabel='Pledge/Backer Ratio (funding per backer)', ylabel='USD Pledged in Million', title= 'Backer Ratio vs Pledge')
Глядя на соотношение сторонников (отсюда сумма обещанной суммы, деленная на общее количество спонсоров), мы видим, что у наиболее успешных проектов соотношение сторонников относительно низкое, а это означает, что каждый спонсор вносит относительно небольшую сумму. Согласно графику, эта сумма колеблется от чуть более 0 долларов США до примерно 700 долларов США. Мы видим, что большинство проектов с относительно высоким уровнем поддержки (
Теперь, когда мы рассмотрели временные рамки, имена, спонсоров и многие другие детали, пора закончить наше небольшое пошаговое руководство. Есть еще много возможных идей и закономерностей, которые нужно раскрыть, но я оставляю это на ваше усмотрение ;-)
6. Заключение
Этот вводный пост о базовой визуализации данных в Python раскрыл некоторые интересные факты о проектах Kickstarter. Подведем итоги:
- Проекты, которые терпят неудачу, в большинстве случаев терпят неудачу. Они «не могут двигаться к синей линии», как обсуждалось в части 3. Все или ничего ;-).
- Количество и средняя цель проектов со временем резко увеличились (по крайней мере, до 2015 года), тогда как предоставленные взносы увеличиваются лишь медленно.
- Имена играют большую роль. Если вы хотите запустить проект, убедитесь, что вы используете точное и краткое название. «Tenacious Exclusive Scott Tolleson Uncle Argh NOIR Mini Qee» может показаться смешным (на самом деле это не так), но ни к чему не приведет.
- Попробуйте запустить свой проект между 12 и 16 часами и НЕ запускайте его в декабре (хорошо, я не на 100% серьезно отношусь к этому).
Надеюсь, вам понравилось это чтение. Я ценю любые отзывы, (конструктивную) критику или другие рекомендации. Спасибо!