Researchers: Elise Berbiers (MA student), Rebecca Chamberlain, Johan Wagemans, Sander Van de Cruys
Replication & extension of experiment 1 in:
Topolinski, S., Erle, T. M., & Reber, R. (2015). Necker’s smile: Immediate affective consequences of early perceptual processes. Cognition, 140, 1–13.
Notes:
"First, we developed and tested a set of pictorial stimuli that were useful for intuitive judgments because they were so degraded that they could only rarely be visually recognized (Bower et al., 1990). We used 30 black-and-white drawings of everyday objects randomly chosen from the inventory by Snodgrass and Vanderwart (1980), with the only constraint being that depicted objects were visually not too simple (e.g., a circle). Following Volz and von Cramon (2006), these stimuli were visually degraded by a filter that masked the black picture on the white background by increasing the white pixels by 75%. These pictures were the object condition (Volz & von Cramon, 2006) since they depicted visually degraded real objects. Then, these pictures were divided into nine equal rectangles (3x3); and these rectangles were randomly rotated within the picture (Volz & von Cramon, 2006; cf., Bower et al., 1990; Wippich, 1994). Thus, these pictures contained the same pixel information as in the object condition and even contained local collinearities (Volz & von Cramon, 2006), but the picture as a whole depicted a physically impossible and thus meaningless object. These pictures were used in the nonobject condition."
Interestingly, there is no effect of condition on density, but there is one on hull. This is probably due to the stimulus generation: the fact that the scrambled are made by scrambling 'tiles' of the source (which may keep the nearest dots together, and density measures the average distance to the nearest dots).
I agree we should address the zygomaticus data. Because the participants liked the inverted condition as much as the gestalt I think this is fairly easily addressed - we would predict the same pattern of results for inverted vs. scrambled as gestalt vs. scrambled (i.e. more positive affect). The authors state in the paper, 'We interpret this differential finding in the way that a success in early Gestalt integration immediately evokes an increase in positive affectivity’ (p.6). I think we could argue the same for the inverted condition, but it is low level grouping/clustering rather than higher-level gestalt that is producing the positive affect. Compact/dense figures are more easily processed, hence liked?
I also have some doubts on the way I analyzed it. Basically, I've done a simple anova of condition on ratings (as in Topolinski), where scramble is significant. Then, to look at the low-level measures, I computed mean rating for each image, and added variables for the extra measures of this image. In this case, an anova of condition on ratings is not significant. If I add hull as predictor, then scramble becomes significant (0.045) and hull is too. If I add density (but not hull), there's no scramble effect, but there is a density effect. Finally, including all three leaves nothing significant.
At first glance I think the first approach (adding mean hull/density etc. for the mean image ratings) is more appropriate, but I agree we should check this with the lab. However, I’m not sure if adding them all in to the same ANCOVA is the right way to go. Aren’t there dependencies between some the measures (i.e. don’t we run into collinearity problems?). Should it then be three separate ANCOVAs with the image statistics as covariates?
If I now use the full dataset to add the low-level measures, so I just add the columns of density/hull to every existing trial, then scramble stays significant (note that it already was significant in this dataset), and additionally scramble*hull interaction and main effect of density become significant. So it seems these low-level measures are not sufficient/appropriate to account for the scramble-effect.
Overall I think there is a good story here but it’s worth presenting to the lab and checking:
In [2]:
import pandas as pd
from pandas import DataFrame
from psychopy import data, core, gui, misc
import numpy as np
import seaborn as sns
#from ggplot import *
from scipy import stats
import statsmodels.formula.api as smf
import statsmodels.api as sm
from __future__ import division
from pivottablejs import pivot_ui
%pylab inline
#plotting params
from matplotlib import rcParams
rcParams['font.family'] = 'ubuntu'
sns.set(style="whitegrid", color_codes=True)
sns.set_context("talk")
In [2]:
# get data file names
files = gui.fileOpenDlg("../data")
dfs = []
for filename in files:
#print(filename)
df = pd.read_table(filename, sep=",") #only Appreciation trials 85
dfs.append(df)
df = pd.concat(dfs, ignore_index=True)
In [3]:
len(df)
Out[3]:
In [ ]:
dfApp = df[pd.notnull(df.images)]
dfNum = df[pd.notnull(df.imgA)]
print('total len:', len(dfApp))
print('total len:', len(dfNum))
In [128]:
dfApp = dfApp.rename(columns={'rating.response': 'rating'})
dfApp = dfApp.rename(columns={'rating.rt': 'rt'})
#add var for img
dfApp.loc[:,"img"]= dfApp.images.str.extract("(\d+)", expand=False).astype(int)
print(dfApp.columns)
dfApp.to_csv("dataLiking.csv")
In [129]:
dfApp = pd.read_csv("dataLiking.csv")
In [7]:
print(len(dfApp['participant'].unique()))
len(dfApp)
Out[7]:
In [22]:
dfApp.groupby(["condition"]).rating.describe()
Out[22]:
In [23]:
dfApp.groupby(["condition"]).rt.describe()
Out[23]:
In [4]:
sns.distplot(dfApp.groupby(['participant']).rating.mean(), bins=20);
In [8]:
#sns.violinplot(x="condition", y="rating.response", data=dfApp);
sns.pointplot(x="condition", y="rating", unit="participant", data=dfApp);
In [25]:
sns.violinplot(x="condition", y="rating", unit="participant", data=dfApp);
In [119]:
# GLM test
model = smf.glm(formula="rating ~ condition", data=dfApp)
results = model.fit()
print(results.summary())
In [5]:
# compute diff score to correlate with PNS score
print(len(dfApp['participant'].unique()))
def diffScore(df):
gestaltm = df[df.condition=='gestalt'].rating.mean()
scrambledm = df[df.condition=='scrambled'].rating.mean()
diff= gestaltm - scrambledm
#df['id'].iloc[0]
dfout = pd.DataFrame(data=[(gestaltm, scrambledm, diff)],\
columns=['gestaltm', 'scrambledm', 'diff'])
return dfout
dfdiff = dfApp.groupby('participant').apply(diffScore)
dfdiff = dfdiff.reset_index()
In [6]:
# add PNS scores
dfPNS = pd.read_table("ScoringPNS.csv", sep=",")
dfPNS = dfPNS.iloc[4:,:]
dfPNS["participant"] = pd.to_numeric(dfPNS["participant"])
dfmerged= pd.merge(dfdiff, dfPNS, how='outer', on='participant')
dfmerged.head()
Out[6]:
In [11]:
sns.jointplot(x="Totaalscore", y="diff", data=dfmerged, kind="reg");
dfmerged["diff"].mean()
Out[11]:
Relevant vars:
In [125]:
dfNum = dfNum.rename(columns={'numResp.corr': 'acc','numResp.rt': 'rt', 'numResp.Alocation': 'Alocation' })
#add var for img
dfNum.loc[:,"img"]= dfNum.imgA.str.extract("(\d+)", expand=False).astype(int)
dfNum.to_csv('dfNum.csv', sep='\t')
In [122]:
dfNum = pd.read_csv("dfNum.csv", sep='\t')
In [123]:
print dfNum.columns
print dfNum.dtypes
In [16]:
sns.pointplot(x="condition", y="acc",unit="participant", data=dfNum);
#sns.axlabel("Condition", "Percentage higher")
#sns.stripplot(x="condition", y="acc",unit="participant", data=dfNum, jitter=True);
In [33]:
model = smf.glm(formula="acc ~ condition", data=dfNum, family=sm.families.Binomial())
results = model.fit()
print(results.summary())
In [126]:
dfimnum = dfNum.groupby(['imgA','imgB','condition'])['acc'].mean()
dfimnum= dfimnum.reset_index()
dfimnum.head()
Out[126]:
In [149]:
ndf = dfNum.pivot_table(index=['participant','img'], columns=['condition'], values='acc')
ndf = ndf.reset_index()
adf = dfApp.pivot_table(index=['participant','img'], columns=['condition'], values='rating')
adf = adf.reset_index()
adf["diffGestalt"] = adf.gestalt - adf.scrambled
adf["diffInv"] = adf.inverted - adf.scrambled
andf = pd.merge(ndf, adf, how='outer', on=['participant', 'img'])
andf = pd.merge(andf, lldf, how = 'left', on = 'img')
andf.head()
Out[149]:
In [157]:
import scipy.stats as stats
stats.ttest_1samp(andf[andf.diffInv.notnull()].diffInv, 0, axis=0)
stats.ttest_1samp(andf.diffGestalt, 0, axis=0)
Out[157]:
In [147]:
sns.jointplot(x="hullDiff", y="diffGestalt", data=andf, kind="reg");
In [161]:
model = smf.glm(formula="diffGestalt ~ hullDiff", data=andf)
results = model.fit()
print(results.summary())
In [133]:
sns.pointplot(x="Gestalt-scrambled", y="diffGestalt", unit="img", data=andf);
In [134]:
sns.pointplot(x="Inverted-scrambled", y="diffInv", unit="img", data=andf);
Using scikit-image:
In [54]:
# prerequisites
import os, re
import matplotlib.pyplot as plt
from skimage.data import data_dir
from skimage.util import img_as_ubyte
from skimage.morphology import skeletonize, convex_hull_image, disk
from skimage import io
from skimage.measure import label, regionprops
from skimage.filters.rank import entropy
from skimage.feature import peak_local_max
def plot_comparison(original, filtered, filter_name):
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 4), sharex=True,
sharey=True)
ax1.imshow(original, cmap=plt.cm.gray)
ax1.set_title('original')
ax1.axis('off')
ax1.set_adjustable('box-forced')
ax2.imshow(filtered, cmap=plt.cm.gray)
ax2.set_title(filter_name)
ax2.axis('off')
ax2.set_adjustable('box-forced')
In [99]:
from itertools import repeat
def distance(p1,p2):
"""Euclidean distance between two points."""
x1,y1 = p1
x2,y2 = p2
return np.hypot(x2 - x1, y2 - y1)
# get data file names
files = gui.fileOpenDlg("../ExpCode/images/)")#, allowed="bmp files (*.bmp)|*.bmp")
d = []
for filename in files:
#print(filename)
stim = io.imread(filename, as_grey=True)
stim = img_as_ubyte(io.imread(filename, as_grey=True))
#fig, ax = plt.subplots()
#ax.imshow(stim, cmap=plt.cm.gray)
#compute convex hull for this img
chull = convex_hull_image(stim == 0)
#plot_comparison(stim, chull, 'convex hull')
label_img = label(chull)
regions_img = regionprops(label_img)
region = regions_img[0]
hull= float(region.convex_area)/(chull.shape[0]*chull.shape[1])
#print "percentage pixels of convex hull image: ", hull
#compute density for this img
stim = numpy.invert(stim) # peaks should be white
# extract peaks aka dots
coords = peak_local_max(stim, min_distance=1)
#print coords
#print len(coords)
#print len(stim[stim>.5])
#compute density as mean of distances to 3 closest neighbors
density = 0
for p1 in coords:
dists = [distance(*pair) for pair in zip(repeat(p1),coords)]
sorted_dists = np.sort(dists)[1:6] # take only distances to 3 closest neighbors
av_dist = np.mean(sorted_dists)
density += av_dist
#print "dists: ", len(dists)
#print "sorted: ", sorted_dists
#print "average: ", av_dist
density /= len(coords)
d.append({'images': filename.split("GestaltAppreciation/ExpCode/")[1] ,'hull': hull, 'density': 1/density})
#print(d)
stims = pd.DataFrame(d)
In [100]:
# make dataset where row is image
dfim = dfApp.groupby(['images','condition'])['rating'].mean()
dfim= dfim.reset_index()
dfim.head()
Out[100]:
In [101]:
dfmerged= pd.merge(dfim, stims, how='outer', on='images')
dfmerged.head()
Out[101]:
In [96]:
dfmerged.to_csv("dataLikingWithLowlevel.csv")
In [102]:
dfApp= pd.merge(dfApp, stims, how='outer', on='images')
In [112]:
# GLM test
model = smf.glm(formula="rating ~ condition * hull * density", data=dfApp)
results = model.fit()
print(results.summary())
In [117]:
dfnummerged= pd.merge(dfimnum, stims, how='left', left_on='imgA', right_on='images')
dfnummerged = dfnummerged.rename(columns={'hull': 'Ahull','density': 'Adensity', 'images': 'imageA' })
dfnummerged= pd.merge(dfnummerged, stims, how='left', left_on='imgB', right_on='images')
dfnummerged = dfnummerged.rename(columns={'hull': 'Bhull','density': 'Bdensity', 'images': 'imageB' })
dfnummerged["hullDiff"]= dfnummerged.Ahull-dfnummerged.Bhull
dfnummerged["densDiff"]= dfnummerged.Adensity-dfnummerged.Bdensity
dfnummerged["img"]= dfnummerged['imgA'].str.extract("(\d+)", expand=False).astype(int)
dfnummerged.tail()
Out[117]:
In [143]:
lldf = dfnummerged[dfnummerged.condition == "Gestalt-scrambled"]
lldf = lldf[['img','hullDiff', 'densDiff']]
lldf.head()
Out[143]:
In [118]:
model = smf.glm(formula="acc ~ hullDiff*condition", data=dfnummerged, family=sm.families.Binomial())
results = model.fit()
print(results.summary())
In [38]:
g = sns.distplot(dfmerged[dfmerged.condition=="inverted"].hull, bins=15, label="inverted");
sns.distplot(dfmerged[dfmerged.condition=="scrambled"].hull, bins=15, label="scrambled");
sns.distplot(dfmerged[dfmerged.condition=="gestalt"].hull, bins=15, label="Gestalt");
plt.legend();
g.set(xlabel='Convex hull area', ylabel='Frequency');
In [39]:
g = sns.pointplot(x="condition", y="hull", data=dfmerged, linestyles=["-"]);
g.set(xlabel='Condition', ylabel='Average convex hull area');
In [40]:
# GLM test
model = smf.glm(formula="hull ~ condition", data=dfmerged)
#model = smf.ols(formula="rating ~ hull", data=dfmerged)
results = model.fit()
print(results.summary())
In [53]:
sns.set_style("whitegrid",{"axes.grid": False})
sns.set_context("talk")
g = sns.lmplot(x="hull", y="rating", hue="condition",\
data=dfmerged, x_estimator=np.mean, palette="Set1");
g.set(xlabel='Convex hull area', ylabel='Rating');
sns.set_style("whitegrid",{"axes.grid": False})
In [114]:
# GLM test
model = smf.glm(formula="rating ~ condition * hull", data=dfmerged)
#model = smf.ols(formula="rating ~ hull", data=dfmerged)
results = model.fit()
print(results.summary())
In [42]:
sns.distplot(dfmerged[dfmerged.condition=="inverted"].density, bins=15, label="inverted");
sns.distplot(dfmerged[dfmerged.condition=="scrambled"].density, bins=15, label="scrambled");
sns.distplot(dfmerged[dfmerged.condition=="gestalt"].density, bins=15, label="Gestalt");
plt.legend();
g.set(xlabel='Density', ylabel='Frequency');
In [43]:
sns.pointplot(x="condition", y="density", data=dfmerged, linestyles=["-"]);
g.set(xlabel='Condition', ylabel='Average density');
In [78]:
# GLM test
model = smf.glm(formula="density ~ condition", data=dfmerged)
#model = smf.ols(formula="rating ~ hull", data=dfmerged)
results = model.fit()
print(results.summary())
In [52]:
sns.set_style("whitegrid",{"axes.grid": False})
sns.set_context("talk")
g= sns.lmplot(x="density", y="rating",\
hue="condition", data=dfmerged, x_estimator=np.mean, palette="Set1");
g.set(xlabel='Density', ylabel='Rating');
In [116]:
# GLM test
model = smf.glm(formula="rating ~ condition * density", data=dfmerged)
#model = smf.ols(formula="rating ~ hull", data=dfmerged)
results = model.fit()
print(results.summary())
In [123]:
# GLM test
model = smf.glm(formula="rating ~ condition * hull * density", data=dfmerged)
#model = smf.ols(formula="rating ~ hull", data=dfmerged)
results = model.fit()
print(results.summary())
In [50]:
sns.jointplot(x="density", y="hull", data=dfmerged, kind="reg")
Out[50]:
Grouping & Numerosity studies:
http://www.ncbi.nlm.nih.gov/pubmed/26451701?dopt=Abstract http://pec.sagepub.com/content/20/5/681.abstract?id=p200681 http://www.sciencedirect.com/science/article/pii/0001691887900588 http://en.cnki.com.cn/Article_en/CJFDTOTAL-XLXB201210005.htm http://www.journalofvision.org/content/13/8/5.full http://www.amsciepub.com/doi/abs/10.2466/04.22.24.27.PMS.111.5.379-398 http://www.sciencedirect.com/science/article/pii/S0010027709001619 http://link.springer.com/article/10.3758/s13414-012-0349-1 http://www.jstor.org/stable/1419789 http://www.plosone.org/article/info%3Adoi%2F10.1371%2Fjournal.pone.0077556#s7 http://link.springer.com/article/10.3758/PBR.16.3.509 http://psycnet.apa.org/journals/xge/67/5/458/ http://www.sciencedirect.com/science/article/pii/0001691887900588 http://journal.frontiersin.org/article/10.3389/fpsyg.2015.01364/full
Stimuli:
Control stimuli:
Procedure:
Other ideas: