Обзор проектов Kickstarter - Простое исследование данных на Python

В данной статье анализируется набор данных из примерно 380 000 проектов Kickstarter. Я проведу вас через простое исследование данных с помощью Python, чтобы раскрыть интересную информацию о проектах Kickstarter и о том, какие атрибуты важны, когда дело доходит до проверки успеха (или неудачи) определенного проекта. В общем, мы рассмотрим базовую визуализацию данных и разработку функций с помощью Python.

1. Общие идеи и мысли

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

2. Исходный набор данных и атрибуты

Набор данных содержит 378.661 проект с Kickstarter и двенадцать начальных атрибутов, связанных с каждым проектом. Давайте посмотрим, какую информацию мы получили о каждом проекте:

  1. name
    Совершенно очевидно, название соответствующего проекта. Например. «Ежедневный кофе»
  2. main_category
    основная категория, к которой относится проект. Например, «Поэзия», «Еда», «Музыка» и многое другое.
  3. категория
    более точное описание основной категории. По сути, это подгруппа main_category (см. 2.). Например. «Напитки», которые будут подгруппой «Еда» из атрибута main_category.
  4. валюта
    валюта проекта (например, доллар США или фунт стерлингов)
  5. запущен
    срок запуска проекта. Это будет важно при дальнейшем анализе таймфреймов.
  6. крайний срок
    крайний срок выполнения проекта. Как и дата запуска, крайний срок станет важным, так же как и дата запуска.
  7. usd_pledged_real
    сумма в долларах США, реализованная в установленный срок.
  8. usd_goal_real
    сумма в долларах США, которую изначально запрашивал проект
  9. спонсоров
    количество сторонников, которые фактически инвестировали в проект.
  10. страна
    страна происхождения проекта
  11. ID
    идентификатор проекта
  12. состояние
    Был ли проект успешным в конце концов? состояние - это категориальная переменная, разделенная на уровни успешный, неудачный, активный, отмененный, неопределенный и приостановленный. Для ясности мы будем смотреть только на то, был ли проект успешным или неудачным (следовательно, мы удалим все проекты, которые не относятся к одному из двух). . Проекты, которые потерпели неудачу или были успешными, составляют около 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')

Здесь есть несколько интересных вещей, на которые стоит обратить внимание:

  1. В целом, больше проектов запускается рано утром (с 0 до 6 часов утра) и больше проектов запускается поздно днем ​​/ вечером (с 16 до 23 часов) по сравнению с временем между 7 и 15 часами.
  2. Соотношение успешных и неудачных проектов меняется в зависимости от времени суток. Мы видим, что соотношение неудачных и успешных проектов составляет почти 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. Подведем итоги:

  1. Проекты, которые терпят неудачу, в большинстве случаев терпят неудачу. Они «не могут двигаться к синей линии», как обсуждалось в части 3. Все или ничего ;-).
  2. Количество и средняя цель проектов со временем резко увеличились (по крайней мере, до 2015 года), тогда как предоставленные взносы увеличиваются лишь медленно.
  3. Имена играют большую роль. Если вы хотите запустить проект, убедитесь, что вы используете точное и краткое название. «Tenacious Exclusive Scott Tolleson Uncle Argh NOIR Mini Qee» может показаться смешным (на самом деле это не так), но ни к чему не приведет.
  4. Попробуйте запустить свой проект между 12 и 16 часами и НЕ запускайте его в декабре (хорошо, я не на 100% серьезно отношусь к этому).

Надеюсь, вам понравилось это чтение. Я ценю любые отзывы, (конструктивную) критику или другие рекомендации. Спасибо!